or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication-flows.mdconfidential-client.mdconfiguration.mderror-handling.mdindex.mdmanaged-identity.mdpublic-client.mdtoken-cache.md
tile.json

confidential-client.mddocs/

Confidential Client Applications

Confidential Client Applications are designed for applications that can securely store client secrets, such as server applications, web APIs, and daemon applications. These applications support advanced authentication flows including client credentials, on-behalf-of, and certificate-based authentication.

Capabilities

ConfidentialClientApplication Class

Main class for creating confidential client applications.

/**
 * Confidential client application class implementing IConfidentialClientApplication interface
 * Suitable for applications that can securely store client secrets
 */
class ConfidentialClientApplication implements IConfidentialClientApplication {
  constructor(configuration: Configuration);
  
  /** Creates the URL of the authorization request */
  getAuthCodeUrl(request: AuthorizationUrlRequest): Promise<string>;
  
  /** Acquires a token by exchanging the authorization code received from the first step of OAuth 2.0 Authorization Code Flow */
  acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
  
  /** Acquires a token silently when a user specifies the account the token is requested for */
  acquireTokenSilent(request: SilentFlowRequest): Promise<AuthenticationResult | null>;
  
  /** Acquires a token by exchanging the refresh token provided for a new set of tokens */
  acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise<AuthenticationResult | null>;
  
  /** Acquires tokens from the authority for the application (not for an end user) */
  acquireTokenByClientCredential(request: ClientCredentialRequest): Promise<AuthenticationResult | null>;
  
  /** Acquires tokens from the authority for the application */
  acquireTokenOnBehalfOf(request: OnBehalfOfRequest): Promise<AuthenticationResult | null>;
  
  /** 
   * Acquires tokens with password grant by exchanging client applications username and password for credentials
   * @deprecated - Use a more secure flow instead
   */
  acquireTokenByUsernamePassword(request: UsernamePasswordRequest): Promise<AuthenticationResult | null>;
  
  /** Gets the token cache for the application */
  getTokenCache(): TokenCache;
  
  /** Returns the logger instance */
  getLogger(): Logger;
  
  /** Replaces the default logger set in configurations with new Logger with new configurations */
  setLogger(logger: Logger): void;
  
  /** Clear the cache */
  clearCache(): void;
  
  /** This extensibility point is meant for Azure SDK to enhance Managed Identity support */
  SetAppTokenProvider(provider: IAppTokenProvider): void;
}

Usage Example:

import { ConfidentialClientApplication } from "@azure/msal-node";

// With client secret
const cca = new ConfidentialClientApplication({
  auth: {
    clientId: "your-client-id",
    clientSecret: "your-client-secret",
    authority: "https://login.microsoftonline.com/your-tenant-id"
  }
});

// With client certificate
const ccaWithCert = new ConfidentialClientApplication({
  auth: {
    clientId: "your-client-id",
    clientCertificate: {
      thumbprintSha256: "cert-thumbprint-sha256",
      privateKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
      x5c: "base64-encoded-certificate"
    },
    authority: "https://login.microsoftonline.com/your-tenant-id"
  }
});

Client Credentials Flow

OAuth 2.0 client credentials flow for app-only authentication without user interaction.

/**
 * Request for client credentials flow (app-only authentication)
 */
type ClientCredentialRequest = {
  /** Array of scopes the application is requesting access to */
  scopes: string[];
  /** Authority URL to override default */
  authority?: string;
  /** Correlation ID for tracking requests */
  correlationId?: string;
  /** Skip cache and force token acquisition from authority */
  skipCache?: boolean;
  /** Client assertion JWT for certificate-based authentication */
  clientAssertion?: string | (() => string);
  /** Additional parameters for token request */
  tokenQueryParameters?: Record<string, string>;
  /** Claims request for additional token claims */
  claims?: string;
};

Usage Example:

// Basic client credentials flow
const clientCredentialRequest = {
  scopes: ["https://graph.microsoft.com/.default"]
};

try {
  const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
  if (response) {
    console.log("Access token:", response.accessToken);
    console.log("Token expires on:", response.expiresOn);
  }
} catch (error) {
  console.error("Client credential authentication failed:", error);
}

// With custom client assertion for certificate rotation
const clientCredentialWithAssertion = {
  scopes: ["https://graph.microsoft.com/.default"],
  clientAssertion: () => {
    // Generate JWT assertion dynamically
    return generateClientAssertion();
  },
  skipCache: true // Always get fresh token
};

const response = await cca.acquireTokenByClientCredential(clientCredentialWithAssertion);

On-Behalf-Of Flow

OAuth 2.0 on-behalf-of flow for middle-tier services to act on behalf of users.

/**
 * Request for on-behalf-of flow for middle-tier services
 */
type OnBehalfOfRequest = {
  /** User assertion (JWT) from the upstream service */
  oboAssertion: string;
  /** Array of scopes the application is requesting access to */
  scopes: string[];
  /** Authority URL to override default */
  authority?: string;
  /** Correlation ID for tracking requests */
  correlationId?: string;
  /** Skip cache and force token acquisition from authority */
  skipCache?: boolean;
  /** Additional parameters for token request */
  tokenQueryParameters?: Record<string, string>;
  /** Claims request for additional token claims */
  claims?: string;
};

Usage Example:

// Middleware service receiving user token and calling downstream API
app.post("/api/data", async (req, res) => {
  const userToken = req.headers.authorization?.replace("Bearer ", "");
  
  if (!userToken) {
    return res.status(401).json({ error: "No token provided" });
  }

  const oboRequest = {
    oboAssertion: userToken,
    scopes: ["https://graph.microsoft.com/user.read"]
  };

  try {
    const response = await cca.acquireTokenOnBehalfOf(oboRequest);
    if (response) {
      // Use the access token to call Microsoft Graph on behalf of user
      const graphResponse = await callMicrosoftGraph(response.accessToken);
      res.json(graphResponse);
    } else {
      res.status(500).json({ error: "Failed to acquire token" });
    }
  } catch (error) {
    console.error("OBO flow failed:", error);
    res.status(500).json({ error: "Authentication failed" });
  }
});

Authorization Code Flow (Server-Side)

Server-side authorization code flow for web applications.

/**
 * Same types as public client but with confidential client capabilities
 */
type AuthorizationUrlRequest = {
  scopes: string[];
  redirectUri: string;
  prompt?: PromptValue;
  state?: string;
  loginHint?: string;
  domainHint?: string;
  extraQueryParameters?: Record<string, string>;
  authority?: string;
  correlationId?: string;
  claims?: string;
};

type AuthorizationCodeRequest = {
  scopes: string[];
  redirectUri: string;
  code: string;
  state?: string;
  authority?: string;
  correlationId?: string;
  claims?: string;
  tokenQueryParameters?: Record<string, string>;
};

Usage Example:

// Express.js web application example
app.get("/auth", async (req, res) => {
  const authCodeUrlParameters = {
    scopes: ["user.read"],
    redirectUri: "https://your-app.com/redirect",
    state: generateState(), // Generate CSRF protection state
  };

  try {
    const authUrl = await cca.getAuthCodeUrl(authCodeUrlParameters);
    res.redirect(authUrl);
  } catch (error) {
    console.error("Failed to generate auth URL:", error);
    res.status(500).send("Authentication initiation failed");
  }
});

app.get("/redirect", async (req, res) => {
  const { code, state } = req.query;
  
  // Validate state parameter for CSRF protection
  if (!validateState(state)) {
    return res.status(400).send("Invalid state parameter");
  }

  const tokenRequest = {
    code: code as string,
    scopes: ["user.read"],
    redirectUri: "https://your-app.com/redirect",
    state: state as string
  };

  try {
    const response = await cca.acquireTokenByCode(tokenRequest);
    
    // Store tokens securely (e.g., encrypted session)
    req.session.accessToken = response.accessToken;
    req.session.account = response.account;
    
    res.redirect("/dashboard");
  } catch (error) {
    console.error("Token exchange failed:", error);
    res.status(500).send("Authentication failed");
  }
});

Silent Token Acquisition

Silent token acquisition for confidential clients with refresh token support.

/**
 * Request for silent token acquisition
 */
type SilentFlowRequest = {
  /** Account to acquire token for (optional for confidential clients) */
  account?: AccountInfo;
  /** Array of scopes the application is requesting access to */
  scopes: string[];
  /** Force refresh from authority instead of using cache */
  forceRefresh?: boolean;
  /** Authority URL to override default */
  authority?: string;
  /** Correlation ID for tracking requests */
  correlationId?: string;
  /** Claims request for additional token claims */
  claims?: string;
};

/**
 * Request for refresh token flow
 */
type RefreshTokenRequest = {
  /** Refresh token to exchange for new tokens */
  refreshToken: string;
  /** Array of scopes the application is requesting access to */
  scopes: string[];
  /** Authority URL to override default */
  authority?: string;
  /** Correlation ID for tracking requests */
  correlationId?: string;
  /** Additional parameters for token request */
  tokenQueryParameters?: Record<string, string>;
  /** Claims request for additional token claims */
  claims?: string;
  /** Force cache refresh for migration scenarios */
  forceCache?: boolean;
};

Usage Example:

// Silent token acquisition
const silentRequest = {
  scopes: ["user.read"],
  forceRefresh: false
};

try {
  const response = await cca.acquireTokenSilent(silentRequest);
  if (response) {
    console.log("Token acquired silently:", response.accessToken);
  }
} catch (error) {
  console.log("Silent acquisition failed, may need user interaction");
}

// Refresh token flow
const refreshRequest = {
  refreshToken: storedRefreshToken,
  scopes: ["user.read", "mail.read"]
};

try {
  const response = await cca.acquireTokenByRefreshToken(refreshRequest);
  if (response) {
    console.log("Token refreshed successfully:", response.accessToken);
    // Update stored tokens
    updateStoredTokens(response);
  }
} catch (error) {
  console.error("Token refresh failed:", error);
}

Username/Password Flow (Deprecated)

Resource Owner Password Credentials (ROPC) flow - deprecated for security reasons.

/**
 * Request for username/password flow (deprecated)
 * @deprecated Use more secure flows like authorization code or device code
 */
type UsernamePasswordRequest = {
  /** Array of scopes the application is requesting access to */
  scopes: string[];
  /** Username for authentication */
  username: string;
  /** Password for authentication */
  password: string;
  /** Authority URL to override default */
  authority?: string;
  /** Correlation ID for tracking requests */
  correlationId?: string;
  /** Additional parameters for token request */
  tokenQueryParameters?: Record<string, string>;
  /** Claims request for additional token claims */
  claims?: string;
};

Usage Example (Not Recommended):

// This flow is deprecated and should be avoided
const usernamePasswordRequest = {
  scopes: ["user.read"],
  username: "user@domain.com",
  password: "user-password"
};

try {
  const response = await cca.acquireTokenByUsernamePassword(usernamePasswordRequest);
  if (response) {
    console.log("Access token:", response.accessToken);
  }
} catch (error) {
  console.error("Username/password authentication failed:", error);
}

Azure SDK Integration

App token provider integration for Azure SDK services.

/**
 * Interface for app token provider (Azure SDK integration)
 */
interface IAppTokenProvider {
  getAppTokenProviderParameters(appTokenProviderParameters: AppTokenProviderParameters): AppTokenProviderResult;
}

type AppTokenProviderParameters = {
  scopes: string[];
  correlationId?: string;
  tenantId?: string;
  claims?: string;
};

type AppTokenProviderResult = {
  accessToken: string;
  expiresOnTimestamp: number;
};

Usage Example:

// Custom app token provider for Azure SDK
class CustomAppTokenProvider implements IAppTokenProvider {
  getAppTokenProviderParameters(params: AppTokenProviderParameters): AppTokenProviderResult {
    // Custom token acquisition logic
    return {
      accessToken: "custom-token",
      expiresOnTimestamp: Date.now() + 3600000 // 1 hour
    };
  }
}

// Set the app token provider
const tokenProvider = new CustomAppTokenProvider();
cca.SetAppTokenProvider(tokenProvider);

Advanced Configuration

Certificate-based Authentication

type NodeAuthOptions = {
  clientId: string;
  authority?: string;
  clientCertificate?: {
    /** SHA-256 thumbprint of the certificate */
    thumbprintSha256?: string;
    /** 
     * @deprecated Use thumbprintSha256 instead
     * SHA-1 thumbprint for backwards compatibility
     */
    thumbprint?: string;
    /** PEM encoded private key */
    privateKey: string;
    /** Base64 encoded X.509 certificate chain */
    x5c?: string;
  };
};

Client Assertion Configuration

type ClientAssertionCallback = () => string;

type NodeAuthOptions = {
  clientId: string;
  authority?: string;
  /** Client assertion JWT or callback function that returns JWT */
  clientAssertion?: string | ClientAssertionCallback;
};

Usage Example:

// Dynamic client assertion generation
const ccaWithAssertion = new ConfidentialClientApplication({
  auth: {
    clientId: "your-client-id",
    clientAssertion: () => {
      // Generate JWT assertion with current timestamp and certificate
      return generateJWTAssertion({
        clientId: "your-client-id",
        audience: "https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token",
        certificate: loadCurrentCertificate()
      });
    },
    authority: "https://login.microsoftonline.com/your-tenant-id"
  }
});