Auth0 Client Authentication Methods: How `#getClientAuth` Prioritizes mTLS, private_key_jwt, and client_secret_post

The Auth0 JavaScript SDK supports three client authentication methods—mTLS, private_key_jwt, and client_secret_post—which the private method #getClientAuth evaluates in that strict security-first order, automatically selecting the strongest available option or throwing MissingClientAuthError if none are configured.

The auth0-auth-js SDK authenticates your application during privileged OAuth 2.0 flows such as Custom Token Exchange and Back-Channel Logout. The AuthClient class encapsulates this logic in the private method #getClientAuth, located in packages/auth0-auth-js/src/auth-client.ts, which implements a deterministic precedence hierarchy based on cryptographic strength.

Supported Client Authentication Methods

The SDK implements three distinct mechanisms for proving client identity to the authorization server. Each requires specific configuration options passed to the AuthClient constructor.

mTLS (Mutual TLS)

mTLS provides the strongest security guarantee by authenticating the client at the TLS transport layer using X.509 certificates. To enable this method, set useMtls: true in your SDK configuration. When activated, #getClientAuth immediately returns client.TlsClientAuth() without evaluating other options. This method requires your HTTP client (configured via customFetch) to present a valid client certificate during the TLS handshake.

private_key_jwt

The private_key_jwt method sends a signed JSON Web Token (JWT) as the client assertion. This requires the clientAssertionSigningKey option containing either a PEM-encoded PKCS#8 private key string or a WebCrypto CryptoKey object. Optionally specify clientAssertionSigningAlg (defaults to RS256) to control the signing algorithm. If you provide a string key, #getClientAuth asynchronously imports it using importPKCS8 before returning client.PrivateKeyJwt(key).

client_secret_post

client_secret_post transmits the raw client secret in the HTTP POST request body. Configure this fallback method by providing the clientSecret option. While simple to implement, this is the least secure option as it exposes the secret in the request payload, making it vulnerable to interception if TLS is compromised.

Prioritization Logic in #getClientAuth

The method implements a strict security hierarchy (high to low) as documented in the source comments at lines 1025–1032 of auth-client.ts. The implementation at lines 1036–1054 executes this precedence:

  1. mTLS first – If this.#options.useMtls is truthy, return client.TlsClientAuth() immediately
  2. Private key JWT second – If clientAssertionSigningKey exists, import it as a CryptoKey if necessary, then return client.PrivateKeyJwt(clientPrivateKey)
  3. Client secret last – Fallback to client.ClientSecretPost(this.#options.clientSecret!) if no prior method is configured

If none of these conditions are met, the method throws MissingClientAuthError to prevent unauthenticated privileged flows.

// packages/auth0-auth-js/src/auth-client.ts (lines 1036-1054)
async #getClientAuth(): Promise<client.ClientAuth> {
  // 1️⃣ mTLS
  if (this.#options.useMtls) {
    return client.TlsClientAuth();
  }

  // 2️⃣ private_key_jwt (import PKCS#8 if needed)
  let clientPrivateKey = this.#options.clientAssertionSigningKey as CryptoKey | undefined;
  if (clientPrivateKey && !(clientPrivateKey instanceof CryptoKey)) {
    clientPrivateKey = await importPKCS8(
      clientPrivateKey,
      this.#options.clientAssertionSigningAlg || 'RS256'
    );
  }

  // 3️⃣ client_secret_post (fallback)
  return clientPrivateKey
    ? client.PrivateKeyJwt(clientPrivateKey)
    : client.ClientSecretPost(this.#options.clientSecret!);
}

The concrete authentication objects (TlsClientAuth, PrivateKeyJwt, ClientSecretPost) are implemented in packages/auth0-auth-js/src/client.ts and returned as client.ClientAuth types compatible with the underlying OAuth library.

Configuration Examples

The following configurations demonstrate how to trigger each authentication path through createAuth0Client:

import createAuth0Client from '@auth0/auth0-auth-js';

// 1️⃣ mTLS authentication
const authMtls = await createAuth0Client({
  domain: 'YOUR_DOMAIN',
  clientId: 'YOUR_CLIENT_ID',
  useMtls: true,
  // Ensure customFetch provides a TLS client certificate
});

// 2️⃣ private_key_jwt authentication
const authJwt = await createAuth0Client({
  domain: 'YOUR_DOMAIN',
  clientId: 'YOUR_CLIENT_ID',
  clientAssertionSigningKey: `-----BEGIN PRIVATE KEY-----
  MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
  -----END PRIVATE KEY-----`,
  clientAssertionSigningAlg: 'RS256', // Optional, defaults to RS256
});

// 3️⃣ client_secret_post authentication (fallback)
const authSecret = await createAuth0Client({
  domain: 'YOUR_DOMAIN',
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
});

All three configurations invoke #getClientAuth internally when performing privileged flows requiring client authentication, such as token exchange or logout notifications.

Summary

  • The auth0-auth-js SDK supports three client authentication methods: mTLS, private_key_jwt, and client_secret_post.
  • #getClientAuth implements a security-first priority order: mTLS → private_key_jwt → client_secret_post.
  • mTLS requires useMtls: true and provides transport-layer authentication via client.TlsClientAuth().
  • private_key_jwt requires clientAssertionSigningKey and uses client.PrivateKeyJwt() after importing the key if necessary.
  • client_secret_post requires clientSecret and serves as the fallback via client.ClientSecretPost().
  • The method throws MissingClientAuthError if no valid authentication configuration is detected.

Frequently Asked Questions

What happens if I configure multiple client authentication methods?

The SDK always selects the first method in the priority chain regardless of additional configuration. If you set useMtls: true while also providing clientAssertionSigningKey, #getClientAuth returns mTLS authentication immediately and ignores the signing key. To use private_key_jwt, you must omit useMtls or set it to false.

How does the SDK handle PEM string keys versus CryptoKey objects for private_key_jwt?

#getClientAuth automatically detects the key type and imports when necessary. If clientAssertionSigningKey is a string, the method calls importPKCS8() with your specified algorithm (defaulting to RS256) to convert it to a WebCrypto CryptoKey before passing it to client.PrivateKeyJwt(). If you provide a CryptoKey directly, it skips the import step.

Why does Auth0 prioritize mTLS over private_key_jwt?

mTLS provides stronger authentication guarantees by binding the client identity to the TLS transport layer rather than application-layer tokens. This prevents token theft and replay attacks, as the authentication is tied to the specific TLS session and certificate. The private_key_jwt method, while cryptographically sound, operates at the application layer where tokens could theoretically be extracted from logs or memory.

Where is the client authentication logic implemented in the source code?

The selection logic resides in packages/auth0-auth-js/src/auth-client.ts (lines 1025–1054), while the concrete authentication factories are defined in packages/auth0-auth-js/src/client.ts. The auth-client.ts file contains the #getClientAuth private method and its prioritization comment block, whereas client.ts exports TlsClientAuth(), PrivateKeyJwt(), and ClientSecretPost() functions that create the actual authentication objects consumed by the OAuth client.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →