From 192f872daa26e0739e9e60073b392a5f696a4272 Mon Sep 17 00:00:00 2001
From: Jialun Cai <jialun.cai@pm.me>
Date: Wed, 12 Mar 2025 16:29:46 +0800
Subject: [PATCH 1/4] Refactor AuthProvider multi-tenant token credential

---
 pkg/azclient/auth.go | 187 +++++++++++++++++++++++++++++--------------
 1 file changed, 129 insertions(+), 58 deletions(-)

diff --git a/pkg/azclient/auth.go b/pkg/azclient/auth.go
index 7acbd1d927..670f1768a2 100644
--- a/pkg/azclient/auth.go
+++ b/pkg/azclient/auth.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
@@ -32,13 +33,17 @@ import (
 )
 
 type AuthProvider struct {
-	ComputeCredential     azcore.TokenCredential
-	NetworkCredential     azcore.TokenCredential
-	MultiTenantCredential azcore.TokenCredential
-	CloudConfig           cloud.Configuration
+	ComputeCredential              azcore.TokenCredential
+	AdditionalComputeClientOptions []func(option *arm.ClientOptions)
+	NetworkCredential              azcore.TokenCredential
+	CloudConfig                    cloud.Configuration
 }
 
-func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, clientOptionsMutFn ...func(option *policy.ClientOptions)) (*AuthProvider, error) {
+func NewAuthProvider(
+	armConfig *ARMClientConfig,
+	config *AzureAuthConfig,
+	clientOptionsMutFn ...func(option *policy.ClientOptions),
+) (*AuthProvider, error) {
 	clientOption, _, err := GetAzCoreClientOption(armConfig)
 	if err != nil {
 		return nil, err
@@ -46,9 +51,11 @@ func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, client
 	for _, fn := range clientOptionsMutFn {
 		fn(clientOption)
 	}
-	var computeCredential azcore.TokenCredential
-	var networkTokenCredential azcore.TokenCredential
-	var multiTenantCredential azcore.TokenCredential
+	var (
+		computeCredential              azcore.TokenCredential
+		networkCredential              azcore.TokenCredential
+		additionalComputeClientOptions []func(option *arm.ClientOptions)
+	)
 
 	// federatedIdentityCredential is used for workload identity federation
 	if aadFederatedTokenFile, enabled := config.GetAzureFederatedTokenFile(); enabled {
@@ -62,6 +69,7 @@ func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, client
 			return nil, err
 		}
 	}
+
 	// managedIdentityCredential is used for managed identity extension
 	if computeCredential == nil && config.UseManagedIdentityExtension {
 		credOptions := &azidentity.ManagedIdentityCredentialOptions{
@@ -79,52 +87,80 @@ func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, client
 			return nil, err
 		}
 		if config.AuxiliaryTokenProvider != nil && IsMultiTenant(armConfig) {
-			networkTokenCredential, err = armauth.NewKeyVaultCredential(
+			// Use AuxiliaryTokenProvider as the network credential
+			networkCredential, err = armauth.NewKeyVaultCredential(
 				computeCredential,
 				config.AuxiliaryTokenProvider.SecretResourceID(),
 			)
 			if err != nil {
 				return nil, fmt.Errorf("create KeyVaultCredential for auxiliary token provider: %w", err)
 			}
+
+			// Additionally, we need to add the auxiliary token to the HTTP header when making requests to the compute resources
+			additionalComputeClientOptions = append(additionalComputeClientOptions, func(option *arm.ClientOptions) {
+				option.PerRetryPolicies = append(option.PerRetryPolicies, armauth.NewAuxiliaryAuthPolicy(
+					[]azcore.TokenCredential{networkCredential},
+					DefaultTokenScopeFor(clientOption.Cloud),
+				))
+			})
 		}
 	}
 
 	// Client secret authentication
 	if computeCredential == nil && len(config.GetAADClientSecret()) > 0 {
-		credOptions := &azidentity.ClientSecretCredentialOptions{
-			ClientOptions: *clientOption,
-		}
-		computeCredential, err = azidentity.NewClientSecretCredential(armConfig.GetTenantID(), config.GetAADClientID(), config.GetAADClientSecret(), credOptions)
-		if err != nil {
-			return nil, err
-		}
 		if IsMultiTenant(armConfig) {
-			credOptions := &azidentity.ClientSecretCredentialOptions{
-				ClientOptions: *clientOption,
-			}
-			networkTokenCredential, err = azidentity.NewClientSecretCredential(armConfig.NetworkResourceTenantID, config.GetAADClientID(), config.AADClientSecret, credOptions)
-			if err != nil {
-				return nil, err
+
+			// Network credential for network resource access
+			{
+				credOptions := &azidentity.ClientSecretCredentialOptions{
+					ClientOptions: *clientOption,
+				}
+				networkCredential, err = azidentity.NewClientSecretCredential(
+					armConfig.NetworkResourceTenantID,
+					config.GetAADClientID(),
+					config.GetAADClientSecret(),
+					credOptions,
+				)
+				if err != nil {
+					return nil, err
+				}
 			}
 
-			credOptions = &azidentity.ClientSecretCredentialOptions{
-				ClientOptions:              *clientOption,
-				AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+			// Compute credential with additional allowed tenants for cross-tenant access
+			{
+				credOptions := &azidentity.ClientSecretCredentialOptions{
+					ClientOptions:              *clientOption,
+					AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+				}
+				computeCredential, err = azidentity.NewClientSecretCredential(
+					armConfig.GetTenantID(),
+					config.GetAADClientID(),
+					config.GetAADClientSecret(),
+					credOptions,
+				)
+				if err != nil {
+					return nil, err
+				}
 			}
-			multiTenantCredential, err = azidentity.NewClientSecretCredential(armConfig.GetTenantID(), config.GetAADClientID(), config.GetAADClientSecret(), credOptions)
+		} else {
+			// Single tenant
+			credOptions := &azidentity.ClientSecretCredentialOptions{
+				ClientOptions: *clientOption,
+			}
+			computeCredential, err = azidentity.NewClientSecretCredential(
+				armConfig.GetTenantID(),
+				config.GetAADClientID(),
+				config.GetAADClientSecret(),
+				credOptions,
+			)
 			if err != nil {
 				return nil, err
 			}
-
 		}
 	}
 
 	// ClientCertificateCredential is used for client certificate
 	if computeCredential == nil && len(config.AADClientCertPath) > 0 {
-		credOptions := &azidentity.ClientCertificateCredentialOptions{
-			ClientOptions:        *clientOption,
-			SendCertificateChain: true,
-		}
 		certData, err := os.ReadFile(config.AADClientCertPath)
 		if err != nil {
 			return nil, fmt.Errorf("reading the client certificate from file %s: %w", config.AADClientCertPath, err)
@@ -133,20 +169,58 @@ func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, client
 		if err != nil {
 			return nil, fmt.Errorf("decoding the client certificate: %w", err)
 		}
-		computeCredential, err = azidentity.NewClientCertificateCredential(armConfig.GetTenantID(), config.GetAADClientID(), certificate, privateKey, credOptions)
-		if err != nil {
-			return nil, err
-		}
+
 		if IsMultiTenant(armConfig) {
-			networkTokenCredential, err = azidentity.NewClientCertificateCredential(armConfig.NetworkResourceTenantID, config.GetAADClientID(), certificate, privateKey, credOptions)
-			if err != nil {
-				return nil, err
+
+			// Network credential for network resource access
+			{
+				credOptions := &azidentity.ClientCertificateCredentialOptions{
+					ClientOptions:        *clientOption,
+					SendCertificateChain: true,
+				}
+				networkCredential, err = azidentity.NewClientCertificateCredential(
+					armConfig.NetworkResourceTenantID,
+					config.GetAADClientID(),
+					certificate,
+					privateKey,
+					credOptions,
+				)
+				if err != nil {
+					return nil, err
+				}
 			}
-			credOptions = &azidentity.ClientCertificateCredentialOptions{
-				ClientOptions:              *clientOption,
-				AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+
+			// Compute credential with additional allowed tenants for cross-tenant access
+			{
+				credOptions := &azidentity.ClientCertificateCredentialOptions{
+					ClientOptions:              *clientOption,
+					AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+					SendCertificateChain:       true,
+				}
+				computeCredential, err = azidentity.NewClientCertificateCredential(
+					armConfig.GetTenantID(),
+					config.GetAADClientID(),
+					certificate,
+					privateKey,
+					credOptions,
+				)
+				if err != nil {
+					return nil, err
+				}
 			}
-			multiTenantCredential, err = azidentity.NewClientCertificateCredential(armConfig.GetTenantID(), config.GetAADClientID(), certificate, privateKey, credOptions)
+		} else {
+			// Single tenant
+			credOptions := &azidentity.ClientCertificateCredentialOptions{
+				ClientOptions:        *clientOption,
+				SendCertificateChain: true,
+			}
+			computeCredential, err = azidentity.NewClientCertificateCredential(
+				armConfig.GetTenantID(),
+				config.GetAADClientID(),
+				certificate,
+				privateKey,
+				credOptions,
+			)
 			if err != nil {
 				return nil, err
 			}
@@ -155,17 +229,21 @@ func NewAuthProvider(armConfig *ARMClientConfig, config *AzureAuthConfig, client
 
 	// UserAssignedIdentityCredentials authentication
 	if computeCredential == nil && len(config.AADMSIDataPlaneIdentityPath) > 0 {
-		computeCredential, err = dataplane.NewUserAssignedIdentityCredential(context.Background(), config.AADMSIDataPlaneIdentityPath, dataplane.WithClientOpts(azcore.ClientOptions{Cloud: clientOption.Cloud}))
+		computeCredential, err = dataplane.NewUserAssignedIdentityCredential(
+			context.Background(),
+			config.AADMSIDataPlaneIdentityPath,
+			dataplane.WithClientOpts(azcore.ClientOptions{Cloud: clientOption.Cloud}),
+		)
 		if err != nil {
 			return nil, err
 		}
 	}
 
 	return &AuthProvider{
-		ComputeCredential:     computeCredential,
-		NetworkCredential:     networkTokenCredential,
-		MultiTenantCredential: multiTenantCredential,
-		CloudConfig:           clientOption.Cloud,
+		ComputeCredential:              computeCredential,
+		AdditionalComputeClientOptions: additionalComputeClientOptions,
+		NetworkCredential:              networkCredential,
+		CloudConfig:                    clientOption.Cloud,
 	}, nil
 }
 
@@ -180,18 +258,11 @@ func (factory *AuthProvider) GetNetworkAzIdentity() azcore.TokenCredential {
 	return factory.ComputeCredential
 }
 
-func (factory *AuthProvider) GetMultiTenantIdentity() azcore.TokenCredential {
-	if factory.MultiTenantCredential != nil {
-		return factory.MultiTenantCredential
-	}
-	return factory.ComputeCredential
-}
-
-func (factory *AuthProvider) IsMultiTenantModeEnabled() bool {
-	return factory.MultiTenantCredential != nil
+func (factory *AuthProvider) DefaultTokenScope() string {
+	return DefaultTokenScopeFor(factory.CloudConfig)
 }
 
-func (factory *AuthProvider) DefaultTokenScope() string {
-	audience := factory.CloudConfig.Services[cloud.ResourceManager].Audience
+func DefaultTokenScopeFor(cloudCfg cloud.Configuration) string {
+	audience := cloudCfg.Services[cloud.ResourceManager].Audience
 	return fmt.Sprintf("%s/.default", strings.TrimRight(audience, "/"))
 }

From 898c74eeb79d89cd1184fb7c0d91f5088a68f92b Mon Sep 17 00:00:00 2001
From: Jialun Cai <jialun.cai@pm.me>
Date: Tue, 18 Mar 2025 11:24:40 +0800
Subject: [PATCH 2/4] Add unit tests

---
 pkg/azclient/auth.go             | 225 ++--------
 pkg/azclient/auth_fake_test.go   | 100 +++++
 pkg/azclient/auth_func.go        | 306 +++++++++++++
 pkg/azclient/auth_func_test.go   | 738 +++++++++++++++++++++++++++++++
 pkg/azclient/auth_option.go      | 150 +++++++
 pkg/azclient/auth_option_test.go |  57 +++
 pkg/azclient/auth_test.go        | 349 +++++++++++++++
 pkg/azclient/go.mod              |   4 +
 pkg/azclient/go.sum              |   2 +
 9 files changed, 1731 insertions(+), 200 deletions(-)
 create mode 100644 pkg/azclient/auth_fake_test.go
 create mode 100644 pkg/azclient/auth_func.go
 create mode 100644 pkg/azclient/auth_func_test.go
 create mode 100644 pkg/azclient/auth_option.go
 create mode 100644 pkg/azclient/auth_option_test.go
 create mode 100644 pkg/azclient/auth_test.go

diff --git a/pkg/azclient/auth.go b/pkg/azclient/auth.go
index 670f1768a2..df370eb042 100644
--- a/pkg/azclient/auth.go
+++ b/pkg/azclient/auth.go
@@ -17,19 +17,17 @@ limitations under the License.
 package azclient
 
 import (
-	"context"
+	"errors"
 	"fmt"
-	"os"
 	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
-	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
-	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
-	"github.com/Azure/msi-dataplane/pkg/dataplane"
+)
 
-	"sigs.k8s.io/cloud-provider-azure/pkg/azclient/armauth"
+var (
+	ErrNoValidAuthMethodFound = errors.New("no valid authentication method found")
 )
 
 type AuthProvider struct {
@@ -42,209 +40,36 @@ type AuthProvider struct {
 func NewAuthProvider(
 	armConfig *ARMClientConfig,
 	config *AzureAuthConfig,
-	clientOptionsMutFn ...func(option *policy.ClientOptions),
+	options ...AuthProviderOption,
 ) (*AuthProvider, error) {
+	opts := defaultAuthProviderOptions()
+	for _, opt := range options {
+		opt(opts)
+	}
+
 	clientOption, _, err := GetAzCoreClientOption(armConfig)
 	if err != nil {
 		return nil, err
 	}
-	for _, fn := range clientOptionsMutFn {
+	for _, fn := range opts.ClientOptionsMutFn {
 		fn(clientOption)
 	}
-	var (
-		computeCredential              azcore.TokenCredential
-		networkCredential              azcore.TokenCredential
-		additionalComputeClientOptions []func(option *arm.ClientOptions)
-	)
-
-	// federatedIdentityCredential is used for workload identity federation
-	if aadFederatedTokenFile, enabled := config.GetAzureFederatedTokenFile(); enabled {
-		computeCredential, err = azidentity.NewWorkloadIdentityCredential(&azidentity.WorkloadIdentityCredentialOptions{
-			ClientOptions: *clientOption,
-			ClientID:      config.GetAADClientID(),
-			TenantID:      armConfig.GetTenantID(),
-			TokenFilePath: aadFederatedTokenFile,
-		})
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// managedIdentityCredential is used for managed identity extension
-	if computeCredential == nil && config.UseManagedIdentityExtension {
-		credOptions := &azidentity.ManagedIdentityCredentialOptions{
-			ClientOptions: *clientOption,
-		}
-		if len(config.UserAssignedIdentityID) > 0 {
-			if strings.Contains(strings.ToUpper(config.UserAssignedIdentityID), "/SUBSCRIPTIONS/") {
-				credOptions.ID = azidentity.ResourceID(config.UserAssignedIdentityID)
-			} else {
-				credOptions.ID = azidentity.ClientID(config.UserAssignedIdentityID)
-			}
-		}
-		computeCredential, err = azidentity.NewManagedIdentityCredential(credOptions)
-		if err != nil {
-			return nil, err
-		}
-		if config.AuxiliaryTokenProvider != nil && IsMultiTenant(armConfig) {
-			// Use AuxiliaryTokenProvider as the network credential
-			networkCredential, err = armauth.NewKeyVaultCredential(
-				computeCredential,
-				config.AuxiliaryTokenProvider.SecretResourceID(),
-			)
-			if err != nil {
-				return nil, fmt.Errorf("create KeyVaultCredential for auxiliary token provider: %w", err)
-			}
-
-			// Additionally, we need to add the auxiliary token to the HTTP header when making requests to the compute resources
-			additionalComputeClientOptions = append(additionalComputeClientOptions, func(option *arm.ClientOptions) {
-				option.PerRetryPolicies = append(option.PerRetryPolicies, armauth.NewAuxiliaryAuthPolicy(
-					[]azcore.TokenCredential{networkCredential},
-					DefaultTokenScopeFor(clientOption.Cloud),
-				))
-			})
-		}
-	}
-
-	// Client secret authentication
-	if computeCredential == nil && len(config.GetAADClientSecret()) > 0 {
-		if IsMultiTenant(armConfig) {
-
-			// Network credential for network resource access
-			{
-				credOptions := &azidentity.ClientSecretCredentialOptions{
-					ClientOptions: *clientOption,
-				}
-				networkCredential, err = azidentity.NewClientSecretCredential(
-					armConfig.NetworkResourceTenantID,
-					config.GetAADClientID(),
-					config.GetAADClientSecret(),
-					credOptions,
-				)
-				if err != nil {
-					return nil, err
-				}
-			}
-
-			// Compute credential with additional allowed tenants for cross-tenant access
-			{
-				credOptions := &azidentity.ClientSecretCredentialOptions{
-					ClientOptions:              *clientOption,
-					AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
-				}
-				computeCredential, err = azidentity.NewClientSecretCredential(
-					armConfig.GetTenantID(),
-					config.GetAADClientID(),
-					config.GetAADClientSecret(),
-					credOptions,
-				)
-				if err != nil {
-					return nil, err
-				}
-			}
-		} else {
-			// Single tenant
-			credOptions := &azidentity.ClientSecretCredentialOptions{
-				ClientOptions: *clientOption,
-			}
-			computeCredential, err = azidentity.NewClientSecretCredential(
-				armConfig.GetTenantID(),
-				config.GetAADClientID(),
-				config.GetAADClientSecret(),
-				credOptions,
-			)
-			if err != nil {
-				return nil, err
-			}
-		}
-	}
-
-	// ClientCertificateCredential is used for client certificate
-	if computeCredential == nil && len(config.AADClientCertPath) > 0 {
-		certData, err := os.ReadFile(config.AADClientCertPath)
-		if err != nil {
-			return nil, fmt.Errorf("reading the client certificate from file %s: %w", config.AADClientCertPath, err)
-		}
-		certificate, privateKey, err := azidentity.ParseCertificates(certData, []byte(config.AADClientCertPassword))
-		if err != nil {
-			return nil, fmt.Errorf("decoding the client certificate: %w", err)
-		}
-
-		if IsMultiTenant(armConfig) {
 
-			// Network credential for network resource access
-			{
-				credOptions := &azidentity.ClientCertificateCredentialOptions{
-					ClientOptions:        *clientOption,
-					SendCertificateChain: true,
-				}
-				networkCredential, err = azidentity.NewClientCertificateCredential(
-					armConfig.NetworkResourceTenantID,
-					config.GetAADClientID(),
-					certificate,
-					privateKey,
-					credOptions,
-				)
-				if err != nil {
-					return nil, err
-				}
-			}
-
-			// Compute credential with additional allowed tenants for cross-tenant access
-			{
-				credOptions := &azidentity.ClientCertificateCredentialOptions{
-					ClientOptions:              *clientOption,
-					AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
-					SendCertificateChain:       true,
-				}
-				computeCredential, err = azidentity.NewClientCertificateCredential(
-					armConfig.GetTenantID(),
-					config.GetAADClientID(),
-					certificate,
-					privateKey,
-					credOptions,
-				)
-				if err != nil {
-					return nil, err
-				}
-			}
-		} else {
-			// Single tenant
-			credOptions := &azidentity.ClientCertificateCredentialOptions{
-				ClientOptions:        *clientOption,
-				SendCertificateChain: true,
-			}
-			computeCredential, err = azidentity.NewClientCertificateCredential(
-				armConfig.GetTenantID(),
-				config.GetAADClientID(),
-				certificate,
-				privateKey,
-				credOptions,
-			)
-			if err != nil {
-				return nil, err
-			}
-		}
-	}
-
-	// UserAssignedIdentityCredentials authentication
-	if computeCredential == nil && len(config.AADMSIDataPlaneIdentityPath) > 0 {
-		computeCredential, err = dataplane.NewUserAssignedIdentityCredential(
-			context.Background(),
-			config.AADMSIDataPlaneIdentityPath,
-			dataplane.WithClientOpts(azcore.ClientOptions{Cloud: clientOption.Cloud}),
-		)
-		if err != nil {
-			return nil, err
-		}
+	aadFederatedTokenFile, federatedTokenEnabled := config.GetAzureFederatedTokenFile()
+	switch {
+	case federatedTokenEnabled:
+		return newAuthProviderWithWorkloadIdentity(aadFederatedTokenFile, armConfig, config, clientOption, opts)
+	case config.UseManagedIdentityExtension:
+		return newAuthProviderWithManagedIdentity(armConfig, config, clientOption, opts)
+	case len(config.GetAADClientSecret()) > 0:
+		return newAuthProviderWithServicePrincipalClientSecret(armConfig, config, clientOption, opts)
+	case len(config.AADClientCertPath) > 0:
+		return newAuthProviderWithServicePrincipalClientCertificate(armConfig, config, clientOption, opts)
+	case len(config.AADMSIDataPlaneIdentityPath) > 0:
+		return newAuthProviderWithUserAssignedIdentity(config, clientOption, opts)
+	default:
+		return nil, ErrNoValidAuthMethodFound
 	}
-
-	return &AuthProvider{
-		ComputeCredential:              computeCredential,
-		AdditionalComputeClientOptions: additionalComputeClientOptions,
-		NetworkCredential:              networkCredential,
-		CloudConfig:                    clientOption.Cloud,
-	}, nil
 }
 
 func (factory *AuthProvider) GetAzIdentity() azcore.TokenCredential {
diff --git a/pkg/azclient/auth_fake_test.go b/pkg/azclient/auth_fake_test.go
new file mode 100644
index 0000000000..b2efdabf39
--- /dev/null
+++ b/pkg/azclient/auth_fake_test.go
@@ -0,0 +1,100 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"context"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/stretchr/testify/assert"
+)
+
+type fakeTokenCredential struct {
+	ID string
+}
+
+func NewFakeTokenCredential(id string) *fakeTokenCredential {
+	return &fakeTokenCredential{ID: id}
+}
+
+func (f *fakeTokenCredential) GetToken(
+	ctx context.Context,
+	options policy.TokenRequestOptions,
+) (azcore.AccessToken, error) {
+	panic("not implemented")
+}
+
+type AuthProviderAssertions func(t testing.TB, authProvider *AuthProvider)
+
+func ApplyAssertions(t testing.TB, authProvider *AuthProvider, assertions []AuthProviderAssertions) {
+	t.Helper()
+
+	for _, assertion := range assertions {
+		assertion(t, authProvider)
+	}
+}
+
+func AssertComputeTokenCredential(expectedID string) AuthProviderAssertions {
+	return func(t testing.TB, authProvider *AuthProvider) {
+		t.Helper()
+
+		assert.NotNil(t, authProvider.ComputeCredential)
+
+		cred, ok := authProvider.ComputeCredential.(*fakeTokenCredential)
+		assert.True(t, ok, "expected a fake token credential")
+		assert.Equal(t, expectedID, cred.ID)
+	}
+}
+
+func AssertNetworkTokenCredential(expectedID string) AuthProviderAssertions {
+	return func(t testing.TB, authProvider *AuthProvider) {
+		t.Helper()
+
+		assert.NotNil(t, authProvider.NetworkCredential)
+
+		cred, ok := authProvider.NetworkCredential.(*fakeTokenCredential)
+		assert.True(t, ok, "expected a fake token credential")
+		assert.Equal(t, expectedID, cred.ID)
+	}
+}
+
+func AssertNilNetworkTokenCredential() AuthProviderAssertions {
+	return func(t testing.TB, authProvider *AuthProvider) {
+		t.Helper()
+
+		assert.Nil(t, authProvider.NetworkCredential)
+	}
+}
+
+func AssertEmptyAdditionalComputeClientOptions() AuthProviderAssertions {
+	return func(t testing.TB, authProvider *AuthProvider) {
+		t.Helper()
+
+		assert.Empty(t, authProvider.AdditionalComputeClientOptions)
+	}
+}
+
+func AssertCloudConfig(expected cloud.Configuration) AuthProviderAssertions {
+	return func(t testing.TB, authProvider *AuthProvider) {
+		t.Helper()
+
+		assert.Equal(t, expected, authProvider.CloudConfig)
+	}
+}
diff --git a/pkg/azclient/auth_func.go b/pkg/azclient/auth_func.go
new file mode 100644
index 0000000000..84482b3600
--- /dev/null
+++ b/pkg/azclient/auth_func.go
@@ -0,0 +1,306 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/Azure/msi-dataplane/pkg/dataplane"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/azclient/armauth"
+)
+
+var (
+	ErrAuxiliaryTokenProviderNotSet = errors.New("auxiliary token provider is not set when multi-tenant is enabled for MSI")
+	ErrNewKeyVaultCredentialFailed  = errors.New("create KeyVaultCredential failed")
+)
+
+// newAuthProviderWithWorkloadIdentity creates a new AuthProvider with workload identity.
+// The caller is responsible for checking if workload identity is enabled.
+// NOTE: it does NOT support multi-tenant scenarios.
+func newAuthProviderWithWorkloadIdentity(
+	aadFederatedTokenFile string,
+	armConfig *ARMClientConfig,
+	config *AzureAuthConfig,
+	clientOptions *policy.ClientOptions,
+	opts *authProviderOptions,
+) (*AuthProvider, error) {
+	computeCredential, err := opts.NewWorkloadIdentityCredentialFn(&azidentity.WorkloadIdentityCredentialOptions{
+		ClientOptions: *clientOptions,
+		ClientID:      config.GetAADClientID(),
+		TenantID:      armConfig.GetTenantID(),
+		TokenFilePath: aadFederatedTokenFile,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &AuthProvider{
+		ComputeCredential: computeCredential,
+		CloudConfig:       clientOptions.Cloud,
+	}, nil
+}
+
+// newAuthProviderWithManagedIdentity creates a new AuthProvider with managed identity.
+// When multi-tenant is enabled, it uses the auxiliary token provider to create a network credential
+// for cross-tenant resource access. If multi-tenant is enabled but the auxiliary token provider
+// is not configured, it returns an error.
+func newAuthProviderWithManagedIdentity(
+	armConfig *ARMClientConfig,
+	config *AzureAuthConfig,
+	clientOptions *policy.ClientOptions,
+	opts *authProviderOptions,
+) (*AuthProvider, error) {
+	credOptions := &azidentity.ManagedIdentityCredentialOptions{
+		ClientOptions: *clientOptions,
+	}
+	if len(config.UserAssignedIdentityID) > 0 {
+		if strings.Contains(strings.ToUpper(config.UserAssignedIdentityID), "/SUBSCRIPTIONS/") {
+			credOptions.ID = azidentity.ResourceID(config.UserAssignedIdentityID)
+		} else {
+			credOptions.ID = azidentity.ClientID(config.UserAssignedIdentityID)
+		}
+	}
+
+	computeCredential, err := opts.NewManagedIdentityCredentialFn(credOptions)
+	if err != nil {
+		return nil, err
+	}
+
+	rv := &AuthProvider{
+		ComputeCredential: computeCredential,
+		CloudConfig:       clientOptions.Cloud,
+	}
+
+	if !IsMultiTenant(armConfig) {
+		return rv, nil
+	}
+
+	if config.AuxiliaryTokenProvider == nil {
+		return nil, ErrAuxiliaryTokenProviderNotSet
+	}
+
+	// Use AuxiliaryTokenProvider as the network credential
+	networkCredential, err := opts.NewKeyVaultCredentialFn(
+		computeCredential,
+		config.AuxiliaryTokenProvider.SecretResourceID(),
+	)
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrNewKeyVaultCredentialFailed, err)
+	}
+
+	// Additionally, we need to add the auxiliary token to the HTTP header when making requests to the compute resources
+	additionalComputeClientOptions := []func(option *arm.ClientOptions){
+		func(option *arm.ClientOptions) {
+			option.PerRetryPolicies = append(option.PerRetryPolicies, armauth.NewAuxiliaryAuthPolicy(
+				[]azcore.TokenCredential{networkCredential},
+				DefaultTokenScopeFor(clientOptions.Cloud),
+			))
+		},
+	}
+	rv.NetworkCredential = networkCredential
+	rv.AdditionalComputeClientOptions = additionalComputeClientOptions
+	return rv, nil
+}
+
+func newAuthProviderWithServicePrincipalClientSecret(
+	armConfig *ARMClientConfig,
+	config *AzureAuthConfig,
+	clientOptions *policy.ClientOptions,
+	opts *authProviderOptions,
+) (*AuthProvider, error) {
+	var (
+		computeCredential azcore.TokenCredential
+		networkCredential azcore.TokenCredential
+	)
+
+	if !IsMultiTenant(armConfig) {
+		// Single tenant
+		credOptions := &azidentity.ClientSecretCredentialOptions{
+			ClientOptions: *clientOptions,
+		}
+		var err error
+		computeCredential, err = opts.NewClientSecretCredentialFn(
+			armConfig.GetTenantID(),
+			config.GetAADClientID(),
+			config.GetAADClientSecret(),
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+
+		return &AuthProvider{
+			ComputeCredential: computeCredential,
+			CloudConfig:       clientOptions.Cloud,
+		}, nil
+	}
+
+	// Network credential for network resource access
+	{
+		credOptions := &azidentity.ClientSecretCredentialOptions{
+			ClientOptions: *clientOptions,
+		}
+		var err error
+		networkCredential, err = opts.NewClientSecretCredentialFn(
+			armConfig.NetworkResourceTenantID,
+			config.GetAADClientID(),
+			config.GetAADClientSecret(),
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Compute credential with additional allowed tenants for cross-tenant access
+	{
+		credOptions := &azidentity.ClientSecretCredentialOptions{
+			ClientOptions:              *clientOptions,
+			AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+		}
+		var err error
+		computeCredential, err = opts.NewClientSecretCredentialFn(
+			armConfig.GetTenantID(),
+			config.GetAADClientID(),
+			config.GetAADClientSecret(),
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return &AuthProvider{
+		ComputeCredential: computeCredential,
+		NetworkCredential: networkCredential,
+		CloudConfig:       clientOptions.Cloud,
+	}, nil
+}
+
+func newAuthProviderWithServicePrincipalClientCertificate(
+	armConfig *ARMClientConfig,
+	config *AzureAuthConfig,
+	clientOptions *policy.ClientOptions,
+	opts *authProviderOptions,
+) (*AuthProvider, error) {
+	certData, err := opts.ReadFileFn(config.AADClientCertPath)
+	if err != nil {
+		return nil, fmt.Errorf("reading the client certificate from file %s: %w", config.AADClientCertPath, err)
+	}
+	certificate, privateKey, err := opts.ParseCertificatesFn(certData, []byte(config.AADClientCertPassword))
+	if err != nil {
+		return nil, fmt.Errorf("decoding the client certificate: %w", err)
+	}
+
+	var (
+		computeCredential azcore.TokenCredential
+		networkCredential azcore.TokenCredential
+	)
+
+	if !IsMultiTenant(armConfig) {
+		// Single tenant
+		credOptions := &azidentity.ClientCertificateCredentialOptions{
+			ClientOptions:        *clientOptions,
+			SendCertificateChain: true,
+		}
+		computeCredential, err = opts.NewClientCertificateCredentialFn(
+			armConfig.GetTenantID(),
+			config.GetAADClientID(),
+			certificate,
+			privateKey,
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+		return &AuthProvider{
+			ComputeCredential: computeCredential,
+			CloudConfig:       clientOptions.Cloud,
+		}, nil
+	}
+
+	// Network credential for network resource access
+	{
+		credOptions := &azidentity.ClientCertificateCredentialOptions{
+			ClientOptions:        *clientOptions,
+			SendCertificateChain: true,
+		}
+		networkCredential, err = opts.NewClientCertificateCredentialFn(
+			armConfig.NetworkResourceTenantID,
+			config.GetAADClientID(),
+			certificate,
+			privateKey,
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Compute credential with additional allowed tenants for cross-tenant access
+	{
+		credOptions := &azidentity.ClientCertificateCredentialOptions{
+			ClientOptions:              *clientOptions,
+			AdditionallyAllowedTenants: []string{armConfig.NetworkResourceTenantID},
+			SendCertificateChain:       true,
+		}
+		computeCredential, err = opts.NewClientCertificateCredentialFn(
+			armConfig.GetTenantID(),
+			config.GetAADClientID(),
+			certificate,
+			privateKey,
+			credOptions,
+		)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return &AuthProvider{
+		ComputeCredential: computeCredential,
+		NetworkCredential: networkCredential,
+		CloudConfig:       clientOptions.Cloud,
+	}, nil
+}
+
+func newAuthProviderWithUserAssignedIdentity(
+	config *AzureAuthConfig,
+	clientOptions *policy.ClientOptions,
+	opts *authProviderOptions,
+) (*AuthProvider, error) {
+	computeCredential, err := opts.NewUserAssignedIdentityCredentialFn(
+		context.Background(),
+		config.AADMSIDataPlaneIdentityPath,
+		dataplane.WithClientOpts(azcore.ClientOptions{Cloud: clientOptions.Cloud}),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	return &AuthProvider{
+		ComputeCredential: computeCredential,
+		CloudConfig:       clientOptions.Cloud,
+	}, nil
+}
diff --git a/pkg/azclient/auth_func_test.go b/pkg/azclient/auth_func_test.go
new file mode 100644
index 0000000000..c105bfdd37
--- /dev/null
+++ b/pkg/azclient/auth_func_test.go
@@ -0,0 +1,738 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"context"
+	"crypto"
+	"crypto/x509"
+	"errors"
+	"fmt"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/Azure/msi-dataplane/pkg/dataplane"
+	"github.com/go-faker/faker/v4"
+	"github.com/stretchr/testify/assert"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/azclient/armauth"
+)
+
+func TestNewAuthProviderWithWorkloadIdentity(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testAADClientID   = faker.UUIDHyphenated()
+		testTenantID      = faker.UUIDHyphenated()
+		testTokenFileName = faker.Word()
+		testARMConfig     = &ARMClientConfig{
+			TenantID: testTenantID,
+		}
+		testAzureAuthConfig = &AzureAuthConfig{
+			AADClientID: testAADClientID,
+		}
+		testCloudConfig                  = cloud.AzurePublic
+		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testErr                          = errors.New("test error")
+	)
+
+	tests := []struct {
+		Name                  string
+		AADFederatedTokenFile string
+		ARMConfig             *ARMClientConfig
+		AuthConfig            *AzureAuthConfig
+		ClientOption          *policy.ClientOptions
+		Opts                  *authProviderOptions
+		Assertions            []AuthProviderAssertions
+		ExpectErr             error
+	}{
+		{
+			Name:                  "error when creating workload identity credential",
+			AADFederatedTokenFile: testTokenFileName,
+			ARMConfig:             testARMConfig,
+			AuthConfig:            testAzureAuthConfig,
+			ClientOption:          testClientOption,
+			Opts: &authProviderOptions{
+				NewWorkloadIdentityCredentialFn: func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:                  "success",
+			AADFederatedTokenFile: testTokenFileName,
+			ARMConfig:             testARMConfig,
+			AuthConfig:            testAzureAuthConfig,
+			ClientOption:          testClientOption,
+			Opts: &authProviderOptions{
+				NewWorkloadIdentityCredentialFn: func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Equal(t, testAADClientID, options.ClientID)
+					assert.Equal(t, testTenantID, options.TenantID)
+					assert.Equal(t, testTokenFileName, options.TokenFilePath)
+
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := newAuthProviderWithWorkloadIdentity(
+				tt.AADFederatedTokenFile,
+				tt.ARMConfig,
+				tt.AuthConfig,
+				tt.ClientOption,
+				tt.Opts,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				assert.ErrorIs(t, err, tt.ExpectErr)
+			} else {
+				assert.NoError(t, err)
+				ApplyAssertions(t, authProvider, tt.Assertions)
+			}
+		})
+	}
+}
+
+func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testTenantID               = faker.UUIDHyphenated()
+		testNetworkTenantID        = faker.UUIDHyphenated()
+		testUserAssignedIdentityID = faker.UUIDHyphenated()
+		testARMConfig              = &ARMClientConfig{
+			TenantID: testTenantID,
+		}
+		testARMConfigMultiTenant = &ARMClientConfig{
+			TenantID:                testTenantID,
+			NetworkResourceTenantID: testNetworkTenantID,
+		}
+		testAzureAuthConfig = &AzureAuthConfig{
+			UserAssignedIdentityID: testUserAssignedIdentityID,
+		}
+		testAzureAuthConfigWithAuxiliaryProvider = &AzureAuthConfig{
+			UserAssignedIdentityID: testUserAssignedIdentityID,
+			AuxiliaryTokenProvider: &AzureAuthAuxiliaryTokenProvider{
+				SubscriptionID: faker.UUIDHyphenated(),
+				ResourceGroup:  faker.Word(),
+				VaultName:      faker.Word(),
+				SecretName:     faker.Word(),
+			},
+		}
+		testCloudConfig                  = cloud.AzurePublic
+		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
+		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
+		testErr                          = errors.New("test error")
+	)
+
+	tests := []struct {
+		Name         string
+		ARMConfig    *ARMClientConfig
+		AuthConfig   *AzureAuthConfig
+		ClientOption *policy.ClientOptions
+		Opts         *authProviderOptions
+		Assertions   []AuthProviderAssertions
+		ExpectErr    error
+	}{
+		{
+			Name:         "error when creating managed identity credential",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success with single tenant",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Equal(t, azidentity.ClientID(testUserAssignedIdentityID), options.ID)
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:         "error with multi-tenant and no auxiliary token provider",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			ExpectErr: ErrAuxiliaryTokenProviderNotSet,
+		},
+		{
+			Name:         "error with multi-tenant when creating KeyVault credential",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfigWithAuxiliaryProvider,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					return testFakeComputeTokenCredential, nil
+				},
+				NewKeyVaultCredentialFn: func(credential azcore.TokenCredential, secretResourceID armauth.SecretResourceID) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: ErrNewKeyVaultCredentialFailed,
+		},
+		{
+			Name:         "success with multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfigWithAuxiliaryProvider,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Equal(t, azidentity.ClientID(testUserAssignedIdentityID), options.ID)
+					return testFakeComputeTokenCredential, nil
+				},
+				NewKeyVaultCredentialFn: func(credential azcore.TokenCredential, secretResourceID armauth.SecretResourceID) (azcore.TokenCredential, error) {
+					assert.Equal(t, testFakeComputeTokenCredential, credential)
+					assert.Equal(t, testAzureAuthConfigWithAuxiliaryProvider.AuxiliaryTokenProvider.SecretResourceID(), secretResourceID)
+					return testFakeNetworkTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := newAuthProviderWithManagedIdentity(
+				tt.ARMConfig,
+				tt.AuthConfig,
+				tt.ClientOption,
+				tt.Opts,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				if errors.Is(err, tt.ExpectErr) {
+					assert.ErrorIs(t, err, tt.ExpectErr)
+				} else {
+					assert.ErrorContains(t, err, tt.ExpectErr.Error())
+				}
+			} else {
+				assert.NoError(t, err)
+				ApplyAssertions(t, authProvider, tt.Assertions)
+			}
+		})
+	}
+}
+
+func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testAADClientID     = faker.UUIDHyphenated()
+		testAADClientSecret = faker.Password()
+		testTenantID        = faker.UUIDHyphenated()
+		testNetworkTenantID = faker.UUIDHyphenated()
+		testARMConfig       = &ARMClientConfig{
+			TenantID: testTenantID,
+		}
+		testARMConfigMultiTenant = &ARMClientConfig{
+			TenantID:                testTenantID,
+			NetworkResourceTenantID: testNetworkTenantID,
+		}
+		testAzureAuthConfig = &AzureAuthConfig{
+			AADClientID:     testAADClientID,
+			AADClientSecret: testAADClientSecret,
+		}
+		testCloudConfig                  = cloud.AzurePublic
+		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
+		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
+		testErr                          = errors.New("test error")
+	)
+
+	tests := []struct {
+		Name         string
+		ARMConfig    *ARMClientConfig
+		AuthConfig   *AzureAuthConfig
+		ClientOption *policy.ClientOptions
+		Opts         *authProviderOptions
+		Assertions   []AuthProviderAssertions
+		ExpectErr    error
+	}{
+		{
+			Name:         "error when creating client secret credential in single tenant",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success with single tenant",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, testTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testAADClientSecret, clientSecret)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Empty(t, options.AdditionallyAllowedTenants)
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:         "error when creating network credential in multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+					if tenantID == testNetworkTenantID {
+						return nil, testErr
+					}
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "error when creating compute credential in multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+					if tenantID == testTenantID {
+						return nil, testErr
+					}
+					return testFakeNetworkTokenCredential, nil
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success with multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testAADClientSecret, clientSecret)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+
+					if tenantID == testNetworkTenantID {
+						assert.Empty(t, options.AdditionallyAllowedTenants)
+						return testFakeNetworkTokenCredential, nil
+					} else if tenantID == testTenantID {
+						assert.Contains(t, options.AdditionallyAllowedTenants, testNetworkTenantID)
+						return testFakeComputeTokenCredential, nil
+					}
+
+					t.Fatalf("unexpected tenant ID: %s", tenantID)
+					return nil, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := newAuthProviderWithServicePrincipalClientSecret(
+				tt.ARMConfig,
+				tt.AuthConfig,
+				tt.ClientOption,
+				tt.Opts,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				assert.ErrorIs(t, err, tt.ExpectErr)
+			} else {
+				assert.NoError(t, err)
+				ApplyAssertions(t, authProvider, tt.Assertions)
+			}
+		})
+	}
+}
+
+func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testAADClientID           = faker.UUIDHyphenated()
+		testAADClientCertPath     = "/path/to/cert.pem"
+		testAADClientCertPassword = faker.Password()
+		testTenantID              = faker.UUIDHyphenated()
+		testNetworkTenantID       = faker.UUIDHyphenated()
+		testARMConfig             = &ARMClientConfig{
+			TenantID: testTenantID,
+		}
+		testARMConfigMultiTenant = &ARMClientConfig{
+			TenantID:                testTenantID,
+			NetworkResourceTenantID: testNetworkTenantID,
+		}
+		testAzureAuthConfig = &AzureAuthConfig{
+			AADClientID:           testAADClientID,
+			AADClientCertPath:     testAADClientCertPath,
+			AADClientCertPassword: testAADClientCertPassword,
+		}
+		testCloudConfig                  = cloud.AzurePublic
+		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
+		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
+		testErr                          = errors.New("test error")
+		testCertData                     = []byte("test-cert-data")
+		testCerts                        = []*x509.Certificate{{}}
+		testPrivateKey                   = struct{ crypto.PrivateKey }{}
+	)
+
+	tests := []struct {
+		Name         string
+		ARMConfig    *ARMClientConfig
+		AuthConfig   *AzureAuthConfig
+		ClientOption *policy.ClientOptions
+		Opts         *authProviderOptions
+		Assertions   []AuthProviderAssertions
+		ExpectErr    error
+	}{
+		{
+			Name:         "error reading certificate file",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "error parsing certificate",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
+					return nil, nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "error creating client certificate credential in single tenant",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					return testCerts, testPrivateKey, nil
+				},
+				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success with single tenant",
+			ARMConfig:    testARMConfig,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					return testCerts, testPrivateKey, nil
+				},
+				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, testTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testCerts, certs)
+					assert.Equal(t, testPrivateKey, key)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.True(t, options.SendCertificateChain)
+					assert.Empty(t, options.AdditionallyAllowedTenants)
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:         "error when creating network credential in multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					return testCerts, testPrivateKey, nil
+				},
+				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					if tenantID == testNetworkTenantID {
+						return nil, testErr
+					}
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "error when creating compute credential in multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					return testCerts, testPrivateKey, nil
+				},
+				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					if tenantID == testTenantID {
+						return nil, testErr
+					}
+					return testFakeNetworkTokenCredential, nil
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success with multi-tenant",
+			ARMConfig:    testARMConfigMultiTenant,
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				ReadFileFn: func(name string) ([]byte, error) {
+					return testCertData, nil
+				},
+				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					return testCerts, testPrivateKey, nil
+				},
+				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testCerts, certs)
+					assert.Equal(t, testPrivateKey, key)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.True(t, options.SendCertificateChain)
+
+					if tenantID == testNetworkTenantID {
+						assert.Empty(t, options.AdditionallyAllowedTenants)
+						return testFakeNetworkTokenCredential, nil
+					} else if tenantID == testTenantID {
+						assert.Contains(t, options.AdditionallyAllowedTenants, testNetworkTenantID)
+						return testFakeComputeTokenCredential, nil
+					}
+
+					t.Fatalf("unexpected tenant ID: %s", tenantID)
+					return nil, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := newAuthProviderWithServicePrincipalClientCertificate(
+				tt.ARMConfig,
+				tt.AuthConfig,
+				tt.ClientOption,
+				tt.Opts,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				assert.ErrorContains(t, err, tt.ExpectErr.Error())
+			} else {
+				assert.NoError(t, err)
+				ApplyAssertions(t, authProvider, tt.Assertions)
+			}
+		})
+	}
+}
+
+func TestNewAuthProviderWithUserAssignedIdentity(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testIdentityPath    = "/var/run/identity.json"
+		testAzureAuthConfig = &AzureAuthConfig{
+			AADMSIDataPlaneIdentityPath: testIdentityPath,
+		}
+		testCloudConfig                  = cloud.AzurePublic
+		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testErr                          = errors.New("test error")
+	)
+
+	tests := []struct {
+		Name         string
+		AuthConfig   *AzureAuthConfig
+		ClientOption *policy.ClientOptions
+		Opts         *authProviderOptions
+		Assertions   []AuthProviderAssertions
+		ExpectErr    error
+	}{
+		{
+			Name:         "error when creating user assigned identity credential",
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewUserAssignedIdentityCredentialFn: func(ctx context.Context, credentialPath string, opts ...dataplane.Option) (azcore.TokenCredential, error) {
+					return nil, testErr
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:         "success",
+			AuthConfig:   testAzureAuthConfig,
+			ClientOption: testClientOption,
+			Opts: &authProviderOptions{
+				NewUserAssignedIdentityCredentialFn: func(ctx context.Context, credentialPath string, opts ...dataplane.Option) (azcore.TokenCredential, error) {
+					assert.Equal(t, testIdentityPath, credentialPath)
+					// Check that the context is not nil
+					assert.NotNil(t, ctx)
+					// Verify we have at least one option passed (the cloud configuration)
+					assert.NotEmpty(t, opts)
+					return testFakeComputeTokenCredential, nil
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := newAuthProviderWithUserAssignedIdentity(
+				tt.AuthConfig,
+				tt.ClientOption,
+				tt.Opts,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				assert.ErrorIs(t, err, tt.ExpectErr)
+			} else {
+				assert.NoError(t, err)
+				ApplyAssertions(t, authProvider, tt.Assertions)
+			}
+		})
+	}
+}
diff --git a/pkg/azclient/auth_option.go b/pkg/azclient/auth_option.go
new file mode 100644
index 0000000000..2597d799c6
--- /dev/null
+++ b/pkg/azclient/auth_option.go
@@ -0,0 +1,150 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"context"
+	"crypto"
+	"crypto/x509"
+	"os"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/Azure/msi-dataplane/pkg/dataplane"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/azclient/armauth"
+)
+
+type (
+	NewWorkloadIdentityCredentialFn func(
+		options *azidentity.WorkloadIdentityCredentialOptions,
+	) (azcore.TokenCredential, error)
+
+	NewManagedIdentityCredentialFn func(
+		options *azidentity.ManagedIdentityCredentialOptions,
+	) (azcore.TokenCredential, error)
+
+	NewClientSecretCredentialFn func(
+		tenantID string,
+		clientID string,
+		clientSecret string,
+		options *azidentity.ClientSecretCredentialOptions,
+	) (azcore.TokenCredential, error)
+
+	NewClientCertificateCredentialFn func(
+		tenantID string,
+		clientID string,
+		certs []*x509.Certificate,
+		key crypto.PrivateKey,
+		options *azidentity.ClientCertificateCredentialOptions,
+	) (azcore.TokenCredential, error)
+
+	NewKeyVaultCredentialFn func(
+		credential azcore.TokenCredential,
+		secretResourceID armauth.SecretResourceID,
+	) (azcore.TokenCredential, error)
+
+	NewUserAssignedIdentityCredentialFn func(
+		ctx context.Context,
+		credentialPath string,
+		opts ...dataplane.Option,
+	) (azcore.TokenCredential, error)
+)
+
+func DefaultNewWorkloadIdentityCredentialFn() NewWorkloadIdentityCredentialFn {
+	return func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+		return azidentity.NewWorkloadIdentityCredential(options)
+	}
+}
+
+func DefaultNewManagedIdentityCredentialFn() NewManagedIdentityCredentialFn {
+	return func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+		return azidentity.NewManagedIdentityCredential(options)
+	}
+}
+
+func DefaultNewClientSecretCredentialFn() NewClientSecretCredentialFn {
+	return func(
+		tenantID string,
+		clientID string,
+		clientSecret string,
+		options *azidentity.ClientSecretCredentialOptions,
+	) (azcore.TokenCredential, error) {
+		return azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, options)
+	}
+}
+func DefaultNewClientCertificateCredentialFn() NewClientCertificateCredentialFn {
+	return func(
+		tenantID string,
+		clientID string,
+		certs []*x509.Certificate,
+		key crypto.PrivateKey,
+		options *azidentity.ClientCertificateCredentialOptions,
+	) (azcore.TokenCredential, error) {
+		return azidentity.NewClientCertificateCredential(tenantID, clientID, certs, key, options)
+	}
+}
+
+func DefaultNewKeyVaultCredentialFn() NewKeyVaultCredentialFn {
+	return func(
+		credential azcore.TokenCredential,
+		secretResourceID armauth.SecretResourceID,
+	) (azcore.TokenCredential, error) {
+		return armauth.NewKeyVaultCredential(credential, secretResourceID)
+	}
+}
+
+func DefaultNewUserAssignedIdentityCredentialFn() NewUserAssignedIdentityCredentialFn {
+	return dataplane.NewUserAssignedIdentityCredential
+}
+
+type AuthProviderOption func(option *authProviderOptions)
+
+type authProviderOptions struct {
+	ClientOptionsMutFn []func(option *policy.ClientOptions)
+	// The following credential factory functions are for testing purposes only
+	// and should not be modified by users of this package
+	NewWorkloadIdentityCredentialFn     NewWorkloadIdentityCredentialFn
+	NewManagedIdentityCredentialFn      NewManagedIdentityCredentialFn
+	NewClientSecretCredentialFn         NewClientSecretCredentialFn
+	NewClientCertificateCredentialFn    NewClientCertificateCredentialFn
+	NewKeyVaultCredentialFn             NewKeyVaultCredentialFn
+	NewUserAssignedIdentityCredentialFn NewUserAssignedIdentityCredentialFn
+	ReadFileFn                          func(name string) ([]byte, error)
+	ParseCertificatesFn                 func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error)
+}
+
+func defaultAuthProviderOptions() *authProviderOptions {
+	return &authProviderOptions{
+		ClientOptionsMutFn:                  []func(option *policy.ClientOptions){},
+		NewWorkloadIdentityCredentialFn:     DefaultNewWorkloadIdentityCredentialFn(),
+		NewManagedIdentityCredentialFn:      DefaultNewManagedIdentityCredentialFn(),
+		NewClientSecretCredentialFn:         DefaultNewClientSecretCredentialFn(),
+		NewClientCertificateCredentialFn:    DefaultNewClientCertificateCredentialFn(),
+		NewKeyVaultCredentialFn:             DefaultNewKeyVaultCredentialFn(),
+		NewUserAssignedIdentityCredentialFn: DefaultNewUserAssignedIdentityCredentialFn(),
+		ReadFileFn:                          os.ReadFile,
+		ParseCertificatesFn:                 azidentity.ParseCertificates,
+	}
+}
+
+func WithClientOptionsMutFn(fn func(option *policy.ClientOptions)) AuthProviderOption {
+	return func(option *authProviderOptions) {
+		option.ClientOptionsMutFn = append(option.ClientOptionsMutFn, fn)
+	}
+}
diff --git a/pkg/azclient/auth_option_test.go b/pkg/azclient/auth_option_test.go
new file mode 100644
index 0000000000..8cee901e56
--- /dev/null
+++ b/pkg/azclient/auth_option_test.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"os"
+	"reflect"
+	"runtime"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/Azure/msi-dataplane/pkg/dataplane"
+	"github.com/stretchr/testify/assert"
+)
+
+func ExpectFuncEqual(t testing.TB, expected, actual any) {
+	t.Helper()
+
+	exp := runtime.FuncForPC(reflect.ValueOf(expected).Pointer()).Name()
+	act := runtime.FuncForPC(reflect.ValueOf(actual).Pointer()).Name()
+	assert.Equal(t, exp, act)
+}
+
+func TestDefaultAuthProviderOptions(t *testing.T) {
+	t.Parallel()
+
+	opts := defaultAuthProviderOptions()
+	assert.NotNil(t, opts)
+	assert.NotNil(t, opts.NewWorkloadIdentityCredentialFn)
+	assert.NotNil(t, opts.NewManagedIdentityCredentialFn)
+	assert.NotNil(t, opts.NewClientSecretCredentialFn)
+	assert.NotNil(t, opts.NewClientCertificateCredentialFn)
+	assert.NotNil(t, opts.NewKeyVaultCredentialFn)
+
+	assert.NotNil(t, opts.NewUserAssignedIdentityCredentialFn)
+	ExpectFuncEqual(t, dataplane.NewUserAssignedIdentityCredential, opts.NewUserAssignedIdentityCredentialFn)
+
+	assert.NotNil(t, opts.ReadFileFn)
+	ExpectFuncEqual(t, os.ReadFile, opts.ReadFileFn)
+
+	assert.NotNil(t, opts.ParseCertificatesFn)
+	ExpectFuncEqual(t, azidentity.ParseCertificates, opts.ParseCertificatesFn)
+}
diff --git a/pkg/azclient/auth_test.go b/pkg/azclient/auth_test.go
new file mode 100644
index 0000000000..4bd115f9b1
--- /dev/null
+++ b/pkg/azclient/auth_test.go
@@ -0,0 +1,349 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package azclient
+
+import (
+	"context"
+	"crypto"
+	"crypto/x509"
+	"errors"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+	"github.com/Azure/msi-dataplane/pkg/dataplane"
+	"github.com/go-faker/faker/v4"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewAuthProvider(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testTenantID                     = faker.UUIDHyphenated()
+		testNetworkTenantID              = faker.UUIDHyphenated()
+		testAADClientID                  = faker.UUIDHyphenated()
+		testAADClientSecret              = faker.Password()
+		testAADClientCertPath            = "/path/to/cert.pem"
+		testIdentityPath                 = "/var/run/identity.json"
+		testFederatedTokenPath           = "/var/run/secrets/token"
+		testCloudConfig                  = cloud.AzurePublic
+		testFakeComputeTokenCredentialID = "fake-compute-token-credential"
+		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
+		testFakeNetworkTokenCredentialID = "fake-network-token-credential"
+		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
+		testErr                          = errors.New("test error")
+	)
+
+	testARMConfig := &ARMClientConfig{
+		TenantID: testTenantID,
+	}
+
+	testARMConfigMultiTenant := &ARMClientConfig{
+		TenantID:                testTenantID,
+		NetworkResourceTenantID: testNetworkTenantID,
+	}
+
+	tests := []struct {
+		Name          string
+		ARMConfig     *ARMClientConfig
+		AuthConfig    *AzureAuthConfig
+		Options       []AuthProviderOption
+		Assertions    []AuthProviderAssertions
+		ExpectErr     error
+		ErrorContains string
+	}{
+		{
+			Name:          "error when GetAzCoreClientOption fails",
+			ARMConfig:     &ARMClientConfig{}, // This will cause validation failure
+			AuthConfig:    &AzureAuthConfig{},
+			ExpectErr:     ErrNoValidAuthMethodFound, // The error won't be this, but we expect an error
+			ErrorContains: "invalid ARM client config",
+		},
+		{
+			Name:       "error when no valid auth method found",
+			ARMConfig:  testARMConfig,
+			AuthConfig: &AzureAuthConfig{},
+			ExpectErr:  ErrNoValidAuthMethodFound,
+		},
+		{
+			Name:      "success with workload identity",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				AADClientID:                           testAADClientID,
+				AADFederatedTokenFile:                 testFederatedTokenPath,
+				UseFederatedWorkloadIdentityExtension: true,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewWorkloadIdentityCredentialFn = func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "success with managed identity",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				UseManagedIdentityExtension: true,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewManagedIdentityCredentialFn = func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "error with managed identity",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				UseManagedIdentityExtension: true,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewManagedIdentityCredentialFn = func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+						return nil, testErr
+					}
+				},
+			},
+			ExpectErr: testErr,
+		},
+		{
+			Name:      "success with service principal client secret",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				AADClientID:     testAADClientID,
+				AADClientSecret: testAADClientSecret,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "success with service principal client certificate",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				AADClientID:       testAADClientID,
+				AADClientCertPath: testAADClientCertPath,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.ReadFileFn = func(name string) ([]byte, error) {
+						return []byte("test-cert-data"), nil
+					}
+					option.ParseCertificatesFn = func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+						return []*x509.Certificate{{}}, struct{ crypto.PrivateKey }{}, nil
+					}
+					option.NewClientCertificateCredentialFn = func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "success with user assigned identity",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				AADMSIDataPlaneIdentityPath: testIdentityPath,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewUserAssignedIdentityCredentialFn = func(ctx context.Context, credentialPath string, opts ...dataplane.Option) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "success with client option mutation",
+			ARMConfig: testARMConfig,
+			AuthConfig: &AzureAuthConfig{
+				AADClientID:     testAADClientID,
+				AADClientSecret: testAADClientSecret,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+				WithClientOptionsMutFn(func(option *policy.ClientOptions) {
+					// Just to test that the mutation function is called
+					option.Retry.MaxRetries = 5
+				}),
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNilNetworkTokenCredential(),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+		{
+			Name:      "success with multi-tenant service principal",
+			ARMConfig: testARMConfigMultiTenant,
+			AuthConfig: &AzureAuthConfig{
+				AADClientID:     testAADClientID,
+				AADClientSecret: testAADClientSecret,
+			},
+			Options: []AuthProviderOption{
+				func(option *authProviderOptions) {
+					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+						if tenantID == testNetworkTenantID {
+							return testFakeNetworkTokenCredential, nil
+						}
+						return testFakeComputeTokenCredential, nil
+					}
+				},
+			},
+			Assertions: []AuthProviderAssertions{
+				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertEmptyAdditionalComputeClientOptions(),
+				AssertCloudConfig(testCloudConfig),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			authProvider, err := NewAuthProvider(
+				tt.ARMConfig,
+				tt.AuthConfig,
+				tt.Options...,
+			)
+
+			if tt.ExpectErr != nil {
+				assert.Error(t, err)
+				if errors.Is(err, tt.ExpectErr) {
+					assert.ErrorIs(t, err, tt.ExpectErr)
+				} else if tt.ErrorContains != "" {
+					assert.ErrorContains(t, err, tt.ErrorContains)
+				}
+				return
+			}
+
+			assert.NoError(t, err)
+			ApplyAssertions(t, authProvider, tt.Assertions)
+
+			// Test additional methods
+			assert.Equal(t, authProvider.ComputeCredential, authProvider.GetAzIdentity())
+
+			networkIdentity := authProvider.GetNetworkAzIdentity()
+			if authProvider.NetworkCredential != nil {
+				assert.Equal(t, authProvider.NetworkCredential, networkIdentity)
+			} else {
+				assert.Equal(t, authProvider.ComputeCredential, networkIdentity)
+			}
+
+			expectedScope := DefaultTokenScopeFor(testCloudConfig)
+			assert.Equal(t, expectedScope, authProvider.DefaultTokenScope())
+		})
+	}
+}
+
+func TestDefaultTokenScopeFor(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		Name     string
+		CloudCfg cloud.Configuration
+		Expected string
+	}{
+		{
+			Name:     "AzurePublic",
+			CloudCfg: cloud.AzurePublic,
+			Expected: "https://management.core.windows.net/.default",
+		},
+		{
+			Name:     "AzureChina",
+			CloudCfg: cloud.AzureChina,
+			Expected: "https://management.core.chinacloudapi.cn/.default",
+		},
+		{
+			Name: "Custom audience with trailing slash",
+			CloudCfg: cloud.Configuration{
+				Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
+					cloud.ResourceManager: {
+						Audience: "https://custom.endpoint.com/",
+					},
+				},
+			},
+			Expected: "https://custom.endpoint.com/.default",
+		},
+		{
+			Name: "Custom audience without trailing slash",
+			CloudCfg: cloud.Configuration{
+				Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
+					cloud.ResourceManager: {
+						Audience: "https://custom.endpoint.com",
+					},
+				},
+			},
+			Expected: "https://custom.endpoint.com/.default",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+			got := DefaultTokenScopeFor(tt.CloudCfg)
+			assert.Equal(t, tt.Expected, got)
+		})
+	}
+}
diff --git a/pkg/azclient/go.mod b/pkg/azclient/go.mod
index beeea9ec2f..cea77874ab 100644
--- a/pkg/azclient/go.mod
+++ b/pkg/azclient/go.mod
@@ -19,9 +19,11 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.7.0
 	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1
 	github.com/Azure/msi-dataplane v0.4.3
+	github.com/go-faker/faker/v4 v4.6.0
 	github.com/google/uuid v1.6.0
 	github.com/onsi/ginkgo/v2 v2.23.0
 	github.com/onsi/gomega v1.36.2
+	github.com/stretchr/testify v1.10.0
 	go.opentelemetry.io/otel v1.35.0
 	go.opentelemetry.io/otel/metric v1.35.0
 	go.uber.org/mock v0.5.0
@@ -38,6 +40,7 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.4.0 // indirect
 	github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/fsnotify/fsnotify v1.8.0 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
@@ -46,6 +49,7 @@ require (
 	github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rogpeppe/go-internal v1.13.1 // indirect
 	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
 	golang.org/x/sys v0.31.0 // indirect
diff --git a/pkg/azclient/go.sum b/pkg/azclient/go.sum
index c1e7d3921c..aa4b09a75b 100644
--- a/pkg/azclient/go.sum
+++ b/pkg/azclient/go.sum
@@ -54,6 +54,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
 github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/go-faker/faker/v4 v4.6.0 h1:6aOPzNptRiDwD14HuAnEtlTa+D1IfFuEHO8+vEFwjTs=
+github.com/go-faker/faker/v4 v4.6.0/go.mod h1:ZmrHuVtTTm2Em9e0Du6CJ9CADaLEzGXW62z1YqFH0m0=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=

From 1679965d250ed8f192055984bc55bcbbb60d86ec Mon Sep 17 00:00:00 2001
From: Jialun Cai <jialun.cai@pm.me>
Date: Tue, 18 Mar 2025 11:27:44 +0800
Subject: [PATCH 3/4] Fix license header

---
 pkg/azclient/auth_func.go | 4 ++++
 pkg/azclient/auth_test.go | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/pkg/azclient/auth_func.go b/pkg/azclient/auth_func.go
index 84482b3600..31e6e2c819 100644
--- a/pkg/azclient/auth_func.go
+++ b/pkg/azclient/auth_func.go
@@ -124,6 +124,8 @@ func newAuthProviderWithManagedIdentity(
 	return rv, nil
 }
 
+// newAuthProviderWithServicePrincipalClientSecret creates a new AuthProvider with service principal client secret.
+// When multi-tenant is enabled, it creates a compute credential with additional allowed tenants for cross-tenant access.
 func newAuthProviderWithServicePrincipalClientSecret(
 	armConfig *ARMClientConfig,
 	config *AzureAuthConfig,
@@ -199,6 +201,8 @@ func newAuthProviderWithServicePrincipalClientSecret(
 	}, nil
 }
 
+// newAuthProviderWithServicePrincipalClientCertificate creates a new AuthProvider with service principal client certificate.
+// When multi-tenant is enabled, it creates a compute credential with additional allowed tenants for cross-tenant access.
 func newAuthProviderWithServicePrincipalClientCertificate(
 	armConfig *ARMClientConfig,
 	config *AzureAuthConfig,
diff --git a/pkg/azclient/auth_test.go b/pkg/azclient/auth_test.go
index 4bd115f9b1..cac5c95ca1 100644
--- a/pkg/azclient/auth_test.go
+++ b/pkg/azclient/auth_test.go
@@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
-	  http://www.apache.org/licenses/LICENSE-2.0
+    http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,

From 47b542f6c93f97ed367bc74bd809ed88f636d479 Mon Sep 17 00:00:00 2001
From: Jialun Cai <jialun.cai@pm.me>
Date: Tue, 18 Mar 2025 16:56:41 +0800
Subject: [PATCH 4/4] Fix lint issues

---
 pkg/azclient/auth_fake_test.go |  22 +++--
 pkg/azclient/auth_func_test.go | 147 +++++++++++++++++++++------------
 pkg/azclient/auth_test.go      |  83 ++++++++++++-------
 3 files changed, 162 insertions(+), 90 deletions(-)

diff --git a/pkg/azclient/auth_fake_test.go b/pkg/azclient/auth_fake_test.go
index b2efdabf39..77586f37fd 100644
--- a/pkg/azclient/auth_fake_test.go
+++ b/pkg/azclient/auth_fake_test.go
@@ -18,7 +18,10 @@ package azclient
 
 import (
 	"context"
+	"fmt"
+	"sync/atomic"
 	"testing"
+	"time"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
@@ -26,17 +29,22 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+var (
+	incCounter = atomic.Int64{}
+)
+
 type fakeTokenCredential struct {
 	ID string
 }
 
-func NewFakeTokenCredential(id string) *fakeTokenCredential {
+func newFakeTokenCredential() *fakeTokenCredential {
+	id := fmt.Sprintf("fake-token-credential-%d-%d", incCounter.Add(1), time.Now().UnixNano())
 	return &fakeTokenCredential{ID: id}
 }
 
 func (f *fakeTokenCredential) GetToken(
-	ctx context.Context,
-	options policy.TokenRequestOptions,
+	_ context.Context,
+	_ policy.TokenRequestOptions,
 ) (azcore.AccessToken, error) {
 	panic("not implemented")
 }
@@ -51,7 +59,7 @@ func ApplyAssertions(t testing.TB, authProvider *AuthProvider, assertions []Auth
 	}
 }
 
-func AssertComputeTokenCredential(expectedID string) AuthProviderAssertions {
+func AssertComputeTokenCredential(tokenCredential *fakeTokenCredential) AuthProviderAssertions {
 	return func(t testing.TB, authProvider *AuthProvider) {
 		t.Helper()
 
@@ -59,11 +67,11 @@ func AssertComputeTokenCredential(expectedID string) AuthProviderAssertions {
 
 		cred, ok := authProvider.ComputeCredential.(*fakeTokenCredential)
 		assert.True(t, ok, "expected a fake token credential")
-		assert.Equal(t, expectedID, cred.ID)
+		assert.Equal(t, tokenCredential.ID, cred.ID)
 	}
 }
 
-func AssertNetworkTokenCredential(expectedID string) AuthProviderAssertions {
+func AssertNetworkTokenCredential(tokenCredential *fakeTokenCredential) AuthProviderAssertions {
 	return func(t testing.TB, authProvider *AuthProvider) {
 		t.Helper()
 
@@ -71,7 +79,7 @@ func AssertNetworkTokenCredential(expectedID string) AuthProviderAssertions {
 
 		cred, ok := authProvider.NetworkCredential.(*fakeTokenCredential)
 		assert.True(t, ok, "expected a fake token credential")
-		assert.Equal(t, expectedID, cred.ID)
+		assert.Equal(t, tokenCredential.ID, cred.ID)
 	}
 }
 
diff --git a/pkg/azclient/auth_func_test.go b/pkg/azclient/auth_func_test.go
index c105bfdd37..68c7bec95d 100644
--- a/pkg/azclient/auth_func_test.go
+++ b/pkg/azclient/auth_func_test.go
@@ -21,7 +21,6 @@ import (
 	"crypto"
 	"crypto/x509"
 	"errors"
-	"fmt"
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
@@ -48,11 +47,10 @@ func TestNewAuthProviderWithWorkloadIdentity(t *testing.T) {
 		testAzureAuthConfig = &AzureAuthConfig{
 			AADClientID: testAADClientID,
 		}
-		testCloudConfig                  = cloud.AzurePublic
-		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
-		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testErr                          = errors.New("test error")
+		testCloudConfig                = cloud.AzurePublic
+		testClientOption               = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
 	)
 
 	tests := []struct {
@@ -72,7 +70,7 @@ func TestNewAuthProviderWithWorkloadIdentity(t *testing.T) {
 			AuthConfig:            testAzureAuthConfig,
 			ClientOption:          testClientOption,
 			Opts: &authProviderOptions{
-				NewWorkloadIdentityCredentialFn: func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+				NewWorkloadIdentityCredentialFn: func(_ *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
 					return nil, testErr
 				},
 			},
@@ -95,7 +93,7 @@ func TestNewAuthProviderWithWorkloadIdentity(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -152,13 +150,11 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 				SecretName:     faker.Word(),
 			},
 		}
-		testCloudConfig                  = cloud.AzurePublic
-		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
-		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
-		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
-		testErr                          = errors.New("test error")
+		testCloudConfig                = cloud.AzurePublic
+		testClientOption               = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testFakeNetworkTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
 	)
 
 	tests := []struct {
@@ -176,7 +172,7 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 			AuthConfig:   testAzureAuthConfig,
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
-				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+				NewManagedIdentityCredentialFn: func(_ *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
 					return nil, testErr
 				},
 			},
@@ -195,7 +191,7 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -207,7 +203,7 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 			AuthConfig:   testAzureAuthConfig,
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
-				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+				NewManagedIdentityCredentialFn: func(_ *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
 					return testFakeComputeTokenCredential, nil
 				},
 			},
@@ -219,10 +215,10 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 			AuthConfig:   testAzureAuthConfigWithAuxiliaryProvider,
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
-				NewManagedIdentityCredentialFn: func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+				NewManagedIdentityCredentialFn: func(_ *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
 					return testFakeComputeTokenCredential, nil
 				},
-				NewKeyVaultCredentialFn: func(credential azcore.TokenCredential, secretResourceID armauth.SecretResourceID) (azcore.TokenCredential, error) {
+				NewKeyVaultCredentialFn: func(_ azcore.TokenCredential, _ armauth.SecretResourceID) (azcore.TokenCredential, error) {
 					return nil, testErr
 				},
 			},
@@ -246,8 +242,8 @@ func TestNewAuthProviderWithManagedIdentity(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
-				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredential),
 				AssertCloudConfig(testCloudConfig),
 			},
 		},
@@ -298,13 +294,11 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 			AADClientID:     testAADClientID,
 			AADClientSecret: testAADClientSecret,
 		}
-		testCloudConfig                  = cloud.AzurePublic
-		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
-		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
-		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
-		testErr                          = errors.New("test error")
+		testCloudConfig                = cloud.AzurePublic
+		testClientOption               = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testFakeNetworkTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
 	)
 
 	tests := []struct {
@@ -322,7 +316,7 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 			AuthConfig:   testAzureAuthConfig,
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
-				NewClientSecretCredentialFn: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+				NewClientSecretCredentialFn: func(_ string, _ string, _ string, _ *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
 					return nil, testErr
 				},
 			},
@@ -344,7 +338,7 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -360,6 +354,11 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 					if tenantID == testNetworkTenantID {
 						return nil, testErr
 					}
+					assert.Equal(t, testTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testAADClientSecret, clientSecret)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Contains(t, options.AdditionallyAllowedTenants, testNetworkTenantID)
 					return testFakeComputeTokenCredential, nil
 				},
 			},
@@ -375,6 +374,11 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 					if tenantID == testTenantID {
 						return nil, testErr
 					}
+					assert.Equal(t, testNetworkTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testAADClientSecret, clientSecret)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.Empty(t, options.AdditionallyAllowedTenants)
 					return testFakeNetworkTokenCredential, nil
 				},
 			},
@@ -404,8 +408,8 @@ func TestNewAuthProviderWithServicePrincipalClientSecret(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
-				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredential),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
 			},
@@ -439,7 +443,7 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 
 	var (
 		testAADClientID           = faker.UUIDHyphenated()
-		testAADClientCertPath     = "/path/to/cert.pem"
+		testAADClientCertPath     = faker.Word()
 		testAADClientCertPassword = faker.Password()
 		testTenantID              = faker.UUIDHyphenated()
 		testNetworkTenantID       = faker.UUIDHyphenated()
@@ -455,16 +459,14 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			AADClientCertPath:     testAADClientCertPath,
 			AADClientCertPassword: testAADClientCertPassword,
 		}
-		testCloudConfig                  = cloud.AzurePublic
-		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
-		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
-		testFakeNetworkTokenCredentialID = fmt.Sprintf("fake-network-token-credential-%s", faker.UUIDHyphenated())
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
-		testErr                          = errors.New("test error")
-		testCertData                     = []byte("test-cert-data")
-		testCerts                        = []*x509.Certificate{{}}
-		testPrivateKey                   = struct{ crypto.PrivateKey }{}
+		testCloudConfig                = cloud.AzurePublic
+		testClientOption               = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testFakeNetworkTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
+		testCertData                   = []byte(faker.Word())
+		testCerts                      = []*x509.Certificate{{}}
+		testPrivateKey                 = struct{ crypto.PrivateKey }{}
 	)
 
 	tests := []struct {
@@ -514,12 +516,21 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
 				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
 					return testCertData, nil
 				},
 				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
 					return testCerts, testPrivateKey, nil
 				},
 				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					assert.Equal(t, testTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testCerts, certs)
+					assert.Equal(t, testPrivateKey, key)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.True(t, options.SendCertificateChain)
 					return nil, testErr
 				},
 			},
@@ -532,9 +543,12 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
 				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
 					return testCertData, nil
 				},
 				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
 					return testCerts, testPrivateKey, nil
 				},
 				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
@@ -549,7 +563,7 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -562,15 +576,29 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
 				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
 					return testCertData, nil
 				},
 				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
 					return testCerts, testPrivateKey, nil
 				},
 				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
 					if tenantID == testNetworkTenantID {
+						assert.Equal(t, testAADClientID, clientID)
+						assert.Equal(t, testCerts, certs)
+						assert.Equal(t, testPrivateKey, key)
+						assert.Equal(t, *testClientOption, options.ClientOptions)
+						assert.True(t, options.SendCertificateChain)
 						return nil, testErr
 					}
+					assert.Equal(t, testTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testCerts, certs)
+					assert.Equal(t, testPrivateKey, key)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.True(t, options.SendCertificateChain)
 					return testFakeComputeTokenCredential, nil
 				},
 			},
@@ -583,15 +611,24 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
 				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
 					return testCertData, nil
 				},
 				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
 					return testCerts, testPrivateKey, nil
 				},
 				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
 					if tenantID == testTenantID {
 						return nil, testErr
 					}
+					assert.Equal(t, testNetworkTenantID, tenantID)
+					assert.Equal(t, testAADClientID, clientID)
+					assert.Equal(t, testCerts, certs)
+					assert.Equal(t, testPrivateKey, key)
+					assert.Equal(t, *testClientOption, options.ClientOptions)
+					assert.True(t, options.SendCertificateChain)
 					return testFakeNetworkTokenCredential, nil
 				},
 			},
@@ -604,9 +641,12 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
 				ReadFileFn: func(name string) ([]byte, error) {
+					assert.Equal(t, testAADClientCertPath, name)
 					return testCertData, nil
 				},
 				ParseCertificatesFn: func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+					assert.Equal(t, testCertData, certData)
+					assert.Equal(t, []byte(testAADClientCertPassword), password)
 					return testCerts, testPrivateKey, nil
 				},
 				NewClientCertificateCredentialFn: func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
@@ -629,8 +669,8 @@ func TestNewAuthProviderWithServicePrincipalClientCertificate(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
-				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredential),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
 			},
@@ -667,11 +707,10 @@ func TestNewAuthProviderWithUserAssignedIdentity(t *testing.T) {
 		testAzureAuthConfig = &AzureAuthConfig{
 			AADMSIDataPlaneIdentityPath: testIdentityPath,
 		}
-		testCloudConfig                  = cloud.AzurePublic
-		testClientOption                 = &policy.ClientOptions{Cloud: testCloudConfig}
-		testFakeComputeTokenCredentialID = fmt.Sprintf("fake-compute-token-credential-%s", faker.UUIDHyphenated())
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testErr                          = errors.New("test error")
+		testCloudConfig                = cloud.AzurePublic
+		testClientOption               = &policy.ClientOptions{Cloud: testCloudConfig}
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
 	)
 
 	tests := []struct {
@@ -687,7 +726,7 @@ func TestNewAuthProviderWithUserAssignedIdentity(t *testing.T) {
 			AuthConfig:   testAzureAuthConfig,
 			ClientOption: testClientOption,
 			Opts: &authProviderOptions{
-				NewUserAssignedIdentityCredentialFn: func(ctx context.Context, credentialPath string, opts ...dataplane.Option) (azcore.TokenCredential, error) {
+				NewUserAssignedIdentityCredentialFn: func(_ context.Context, _ string, _ ...dataplane.Option) (azcore.TokenCredential, error) {
 					return nil, testErr
 				},
 			},
@@ -708,7 +747,7 @@ func TestNewAuthProviderWithUserAssignedIdentity(t *testing.T) {
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
diff --git a/pkg/azclient/auth_test.go b/pkg/azclient/auth_test.go
index cac5c95ca1..9155d6500e 100644
--- a/pkg/azclient/auth_test.go
+++ b/pkg/azclient/auth_test.go
@@ -36,19 +36,18 @@ func TestNewAuthProvider(t *testing.T) {
 	t.Parallel()
 
 	var (
-		testTenantID                     = faker.UUIDHyphenated()
-		testNetworkTenantID              = faker.UUIDHyphenated()
-		testAADClientID                  = faker.UUIDHyphenated()
-		testAADClientSecret              = faker.Password()
-		testAADClientCertPath            = "/path/to/cert.pem"
-		testIdentityPath                 = "/var/run/identity.json"
-		testFederatedTokenPath           = "/var/run/secrets/token"
-		testCloudConfig                  = cloud.AzurePublic
-		testFakeComputeTokenCredentialID = "fake-compute-token-credential"
-		testFakeComputeTokenCredential   = NewFakeTokenCredential(testFakeComputeTokenCredentialID)
-		testFakeNetworkTokenCredentialID = "fake-network-token-credential"
-		testFakeNetworkTokenCredential   = NewFakeTokenCredential(testFakeNetworkTokenCredentialID)
-		testErr                          = errors.New("test error")
+		testTenantID                   = faker.UUIDHyphenated()
+		testNetworkTenantID            = faker.UUIDHyphenated()
+		testAADClientID                = faker.UUIDHyphenated()
+		testAADClientSecret            = faker.Password()
+		testAADClientCertPath          = faker.Word()
+		testAADClientCertPassword      = faker.Password()
+		testIdentityPath               = faker.Word()
+		testFederatedTokenPath         = faker.Word()
+		testCloudConfig                = cloud.AzurePublic
+		testFakeComputeTokenCredential = newFakeTokenCredential()
+		testFakeNetworkTokenCredential = newFakeTokenCredential()
+		testErr                        = errors.New("test error")
 	)
 
 	testARMConfig := &ARMClientConfig{
@@ -92,13 +91,13 @@ func TestNewAuthProvider(t *testing.T) {
 			},
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
-					option.NewWorkloadIdentityCredentialFn = func(options *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					option.NewWorkloadIdentityCredentialFn = func(_ *azidentity.WorkloadIdentityCredentialOptions) (azcore.TokenCredential, error) {
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -112,13 +111,13 @@ func TestNewAuthProvider(t *testing.T) {
 			},
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
-					option.NewManagedIdentityCredentialFn = func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					option.NewManagedIdentityCredentialFn = func(_ *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -132,7 +131,7 @@ func TestNewAuthProvider(t *testing.T) {
 			},
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
-					option.NewManagedIdentityCredentialFn = func(options *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
+					option.NewManagedIdentityCredentialFn = func(_ *azidentity.ManagedIdentityCredentialOptions) (azcore.TokenCredential, error) {
 						return nil, testErr
 					}
 				},
@@ -149,12 +148,16 @@ func TestNewAuthProvider(t *testing.T) {
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
 					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+						assert.Equal(t, testTenantID, tenantID)
+						assert.Equal(t, testAADClientID, clientID)
+						assert.Equal(t, testAADClientSecret, clientSecret)
+						assert.Equal(t, testCloudConfig, options.ClientOptions.Cloud)
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -164,24 +167,32 @@ func TestNewAuthProvider(t *testing.T) {
 			Name:      "success with service principal client certificate",
 			ARMConfig: testARMConfig,
 			AuthConfig: &AzureAuthConfig{
-				AADClientID:       testAADClientID,
-				AADClientCertPath: testAADClientCertPath,
+				AADClientID:           testAADClientID,
+				AADClientCertPath:     testAADClientCertPath,
+				AADClientCertPassword: testAADClientCertPassword,
 			},
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
+					testCertData := []byte(faker.Word())
 					option.ReadFileFn = func(name string) ([]byte, error) {
-						return []byte("test-cert-data"), nil
+						assert.Equal(t, testAADClientCertPath, name)
+						return testCertData, nil
 					}
 					option.ParseCertificatesFn = func(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
+						assert.Equal(t, testCertData, certData)
+						assert.Equal(t, []byte(testAADClientCertPassword), password)
 						return []*x509.Certificate{{}}, struct{ crypto.PrivateKey }{}, nil
 					}
-					option.NewClientCertificateCredentialFn = func(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+					option.NewClientCertificateCredentialFn = func(tenantID, clientID string, _ []*x509.Certificate, _ crypto.PrivateKey, options *azidentity.ClientCertificateCredentialOptions) (azcore.TokenCredential, error) {
+						assert.Equal(t, testTenantID, tenantID)
+						assert.Equal(t, testAADClientID, clientID)
+						assert.Equal(t, testCloudConfig, options.ClientOptions.Cloud)
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -195,13 +206,14 @@ func TestNewAuthProvider(t *testing.T) {
 			},
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
-					option.NewUserAssignedIdentityCredentialFn = func(ctx context.Context, credentialPath string, opts ...dataplane.Option) (azcore.TokenCredential, error) {
+					option.NewUserAssignedIdentityCredentialFn = func(_ context.Context, credentialPath string, _ ...dataplane.Option) (azcore.TokenCredential, error) {
+						assert.Equal(t, testIdentityPath, credentialPath)
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -217,6 +229,10 @@ func TestNewAuthProvider(t *testing.T) {
 			Options: []AuthProviderOption{
 				func(option *authProviderOptions) {
 					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
+						assert.Equal(t, testTenantID, tenantID)
+						assert.Equal(t, testAADClientID, clientID)
+						assert.Equal(t, testAADClientSecret, clientSecret)
+						assert.Equal(t, testCloudConfig, options.ClientOptions.Cloud)
 						return testFakeComputeTokenCredential, nil
 					}
 				},
@@ -226,7 +242,7 @@ func TestNewAuthProvider(t *testing.T) {
 				}),
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
 				AssertNilNetworkTokenCredential(),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
@@ -243,15 +259,24 @@ func TestNewAuthProvider(t *testing.T) {
 				func(option *authProviderOptions) {
 					option.NewClientSecretCredentialFn = func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (azcore.TokenCredential, error) {
 						if tenantID == testNetworkTenantID {
+							assert.Equal(t, testNetworkTenantID, tenantID)
+							assert.Equal(t, testAADClientID, clientID)
+							assert.Equal(t, testAADClientSecret, clientSecret)
+							assert.Equal(t, testCloudConfig, options.ClientOptions.Cloud)
 							return testFakeNetworkTokenCredential, nil
 						}
+
+						assert.Equal(t, testTenantID, tenantID)
+						assert.Equal(t, testAADClientID, clientID)
+						assert.Equal(t, testAADClientSecret, clientSecret)
+						assert.Equal(t, testCloudConfig, options.ClientOptions.Cloud)
 						return testFakeComputeTokenCredential, nil
 					}
 				},
 			},
 			Assertions: []AuthProviderAssertions{
-				AssertComputeTokenCredential(testFakeComputeTokenCredentialID),
-				AssertNetworkTokenCredential(testFakeNetworkTokenCredentialID),
+				AssertComputeTokenCredential(testFakeComputeTokenCredential),
+				AssertNetworkTokenCredential(testFakeNetworkTokenCredential),
 				AssertEmptyAdditionalComputeClientOptions(),
 				AssertCloudConfig(testCloudConfig),
 			},