diff --git a/plugins/azure/types.ts b/plugins/azure/types.ts index 41832be16..fc475a1e7 100644 --- a/plugins/azure/types.ts +++ b/plugins/azure/types.ts @@ -1,6 +1,6 @@ export interface AzureCredentials { resourceName: string; - azureAuthMode: 'apiKey' | 'entra' | 'managed'; + azureAuthMode: 'apiKey' | 'entra' | 'managed' | 'azure_cli'; apiKey?: string; clientId?: string; clientSecret?: string; diff --git a/plugins/azure/utils.ts b/plugins/azure/utils.ts index 222beefdb..9f6b0ce7d 100644 --- a/plugins/azure/utils.ts +++ b/plugins/azure/utils.ts @@ -102,6 +102,42 @@ export async function getAzureManagedIdentityToken( return result; } +export function getAzureCliToken( + scope = 'https://cognitiveservices.azure.com/.default', + check: string +): { token: string; error: string | null } { + const result: { token: string; error: string | null } = { + token: '', + error: null, + }; + + try { + // Note: Azure CLI auth only works in Node.js runtime + // This will not work in Cloudflare Workers or other edge runtimes + if (typeof process === 'undefined' || !process.versions?.node) { + result.error = 'Azure CLI authentication requires Node.js runtime'; + return result; + } + + const { execSync } = require('child_process'); + + // Execute Azure CLI command to get access token + const command = `az account get-access-token --resource ${scope.replace('/.default', '')}`; + const output = execSync(command, { encoding: 'utf-8' }); + + const tokenData = JSON.parse(output); + result.token = tokenData.accessToken; + } catch (error: any) { + result.error = error?.message || String(error); + console.error('getAzureCliToken error: ', result.error); + console.error( + 'Make sure Azure CLI is installed and you are logged in using "az login"' + ); + } + + return result; +} + export const getAccessToken = async ( credentials: AzureCredentials, check: string, @@ -142,5 +178,9 @@ export const getAccessToken = async ( ); } + if (azureAuthMode === 'azure_cli') { + tokenResult = getAzureCliToken(scope, check); + } + return tokenResult; }; diff --git a/src/providers/azure-ai-inference/api.ts b/src/providers/azure-ai-inference/api.ts index f169c15ab..15c443581 100644 --- a/src/providers/azure-ai-inference/api.ts +++ b/src/providers/azure-ai-inference/api.ts @@ -5,6 +5,7 @@ import { getAccessTokenFromEntraId, getAzureManagedIdentityToken, getAzureWorkloadIdentityToken, + getAzureCliToken, } from '../azure-openai/utils'; import { ProviderAPIConfig } from '../types'; @@ -131,6 +132,16 @@ const AzureAIInferenceAPI: ProviderAPIConfig = { } } + // Azure CLI authentication mode - only available in Node.js runtime + if (azureAuthMode === 'azure_cli' && runtime === 'node') { + const scope = 'https://cognitiveservices.azure.com/.default'; + const accessToken = getAzureCliToken(scope); + if (accessToken) { + headers['Authorization'] = `Bearer ${accessToken}`; + return headers; + } + } + if (apiKey) { headers['Authorization'] = `Bearer ${apiKey}`; return headers; diff --git a/src/providers/azure-openai/api.ts b/src/providers/azure-openai/api.ts index 7802e11c5..358920dd9 100644 --- a/src/providers/azure-openai/api.ts +++ b/src/providers/azure-openai/api.ts @@ -4,6 +4,7 @@ import { getAccessTokenFromEntraId, getAzureManagedIdentityToken, getAzureWorkloadIdentityToken, + getAzureCliToken, } from './utils'; import { getRuntimeKey } from 'hono/adapter'; @@ -77,6 +78,16 @@ const AzureOpenAIAPIConfig: ProviderAPIConfig = { } } } + // Azure CLI authentication mode - only available in Node.js runtime + if (azureAuthMode === 'azure_cli' && runtime === 'node') { + const scope = 'https://cognitiveservices.azure.com/.default'; + const accessToken = getAzureCliToken(scope); + if (accessToken) { + return { + Authorization: `Bearer ${accessToken}`, + }; + } + } const headersObj: Record = { 'api-key': `${apiKey}`, }; diff --git a/src/providers/azure-openai/utils.ts b/src/providers/azure-openai/utils.ts index 8620686f3..6d95139ab 100644 --- a/src/providers/azure-openai/utils.ts +++ b/src/providers/azure-openai/utils.ts @@ -1,6 +1,7 @@ import { AZURE_OPEN_AI } from '../../globals'; import { OpenAIErrorResponseTransform } from '../openai/utils'; import { ErrorResponse } from '../types'; +import { execSync } from 'child_process'; export async function getAccessTokenFromEntraId( tenantId: string, @@ -105,6 +106,25 @@ export async function getAzureWorkloadIdentityToken( } } +export function getAzureCliToken( + scope = 'https://cognitiveservices.azure.com/.default' +): string | undefined { + try { + // Execute Azure CLI command to get access token + const command = `az account get-access-token --resource ${scope.replace('/.default', '')}`; + const output = execSync(command, { encoding: 'utf-8' }); + + const tokenData = JSON.parse(output); + return tokenData.accessToken; + } catch (error: any) { + console.error('getAzureCliToken error: ', error?.message || error); + console.error( + 'Make sure Azure CLI is installed and you are logged in using "az login"' + ); + return undefined; + } +} + export const AzureOpenAIFinetuneResponseTransform = ( response: Response | ErrorResponse, responseStatus: number diff --git a/src/types/requestBody.ts b/src/types/requestBody.ts index 452e2b83a..401efc867 100644 --- a/src/types/requestBody.ts +++ b/src/types/requestBody.ts @@ -62,6 +62,7 @@ export interface Options { deploymentId?: string; apiVersion?: string; adAuth?: string; + /** Azure authentication mode: 'apiKey' | 'entra' | 'managed' | 'workload' | 'azure_cli' */ azureAuthMode?: string; azureManagedClientId?: string; azureWorkloadClientId?: string; @@ -193,6 +194,7 @@ export interface Targets { deploymentId?: string; apiVersion?: string; adAuth?: string; + /** Azure authentication mode: 'apiKey' | 'entra' | 'managed' | 'workload' | 'azure_cli' */ azureAuthMode?: string; azureManagedClientId?: string; azureEntraClientId?: string; @@ -475,6 +477,7 @@ export interface ShortConfig { deploymentId?: string; workersAiAccountId?: string; apiVersion?: string; + /** Azure authentication mode: 'apiKey' | 'entra' | 'managed' | 'workload' | 'azure_cli' */ azureAuthMode?: string; azureManagedClientId?: string; azureEntraClientId?: string;