import {
DefaultAzureCredential,
DefaultAzureCredentialOptions,
ChainedTokenCredential,
AzureCliCredential,
AzureDeveloperCliCredential,
AzurePowerShellCredential,
EnvironmentCredential,
TokenCredential
} from '@azure/identity';
import { getConfig } from './config.js';
import { logger } from './logger.js';
let credentialInstance: TokenCredential | null = null;
export function getCredential(): TokenCredential {
if (credentialInstance) return credentialInstance;
const config = getConfig();
if (!config.includeProductionCredentials) {
// Dev environment: use only local dev credentials (excludes Managed/Workload Identity)
credentialInstance = new ChainedTokenCredential(
new EnvironmentCredential(),
new AzureCliCredential({ tenantId: config.azureTenantId }),
new AzureDeveloperCliCredential({ tenantId: config.azureTenantId }),
new AzurePowerShellCredential({ tenantId: config.azureTenantId })
);
} else {
// Production: use full DefaultAzureCredential chain
const options: DefaultAzureCredentialOptions = {};
if (config.azureTenantId) {
options.tenantId = config.azureTenantId;
}
credentialInstance = new DefaultAzureCredential(options);
}
logger.debug('Azure credential initialized', {
tenantId: config.azureTenantId,
productionCredentials: config.includeProductionCredentials
});
return credentialInstance;
}
export async function validateCredential(): Promise<boolean> {
try {
const credential = getCredential();
await credential.getToken('https://management.azure.com/.default');
return true;
} catch (error) {
logger.warn('Credential validation failed', {
error: error instanceof Error ? error.message : String(error)
});
return false;
}
}
export function resetCredential(): void {
credentialInstance = null;
}
export async function getAccessToken(scope: string = 'https://management.azure.com/.default'): Promise<string | null> {
try {
const credential = getCredential();
const token = await credential.getToken(scope);
return token?.token ?? null;
} catch {
return null;
}
}