mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-03-15 20:26:33 +01:00
🔐 feat: Implement Entra ID authentication for Azure OpenAI integration
- Added support for Entra ID authentication in OpenAIClient and related services. - Updated header management to conditionally use Entra ID access tokens or API keys based on environment configuration. - Introduced utility functions for Entra ID token retrieval and credential management. - Enhanced tests to verify Entra ID authentication flow and its integration with Azure configurations.
This commit is contained in:
parent
a1471c2f37
commit
9288e84454
9 changed files with 212 additions and 18 deletions
|
|
@ -1549,4 +1549,22 @@ describe('getOpenAIConfig', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entra ID Authentication', () => {
|
||||
it('should handle Entra ID authentication in Azure configuration', () => {
|
||||
const azure = {
|
||||
azureOpenAIApiInstanceName: 'test-instance',
|
||||
azureOpenAIApiDeploymentName: 'test-deployment',
|
||||
azureOpenAIApiVersion: '2023-05-15',
|
||||
azureOpenAIApiKey: 'entra-id-placeholder',
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, { azure });
|
||||
|
||||
expect(result.llmConfig).toMatchObject({
|
||||
...azure,
|
||||
model: 'test-deployment',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type {
|
|||
UserKeyValues,
|
||||
} from '~/types';
|
||||
import { createHandleLLMNewToken } from '~/utils/generators';
|
||||
import { getAzureCredentials } from '~/utils/azure';
|
||||
import { getAzureCredentials, getEntraIdAccessToken, shouldUseEntraId } from '~/utils/azure';
|
||||
import { isUserProvided } from '~/utils/common';
|
||||
import { resolveHeaders } from '~/utils/env';
|
||||
import { getOpenAIConfig } from './config';
|
||||
|
|
@ -110,12 +110,30 @@ export const initializeOpenAI = async ({
|
|||
if (!clientOptions.headers) {
|
||||
clientOptions.headers = {};
|
||||
}
|
||||
clientOptions.headers['api-key'] = apiKey;
|
||||
if (shouldUseEntraId()) {
|
||||
clientOptions.headers['Authorization'] = `Bearer ${await getEntraIdAccessToken()}`;
|
||||
} else {
|
||||
clientOptions.headers['api-key'] = apiKey || '';
|
||||
}
|
||||
} else {
|
||||
apiKey = azureOptions.azureOpenAIApiKey || '';
|
||||
clientOptions.azure = azureOptions;
|
||||
if (shouldUseEntraId()) {
|
||||
apiKey = 'entra-id-placeholder';
|
||||
clientOptions.headers['Authorization'] = `Bearer ${await getEntraIdAccessToken()}`;
|
||||
}
|
||||
}
|
||||
} else if (isAzureOpenAI) {
|
||||
clientOptions.azure =
|
||||
userProvidesKey && userValues?.apiKey ? JSON.parse(userValues.apiKey) : getAzureCredentials();
|
||||
apiKey = clientOptions.azure ? clientOptions.azure.azureOpenAIApiKey : undefined;
|
||||
if (shouldUseEntraId()) {
|
||||
clientOptions.headers = {
|
||||
...clientOptions.headers,
|
||||
Authorization: `Bearer ${await getEntraIdAccessToken()}`,
|
||||
};
|
||||
} else {
|
||||
apiKey = clientOptions.azure ? clientOptions.azure.azureOpenAIApiKey : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (userProvidesKey && !apiKey) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { isEnabled } from './common';
|
||||
import type { AzureOptions, GenericClient } from '~/types';
|
||||
import { DefaultAzureCredential } from '@azure/identity';
|
||||
import { logger } from '@librechat/data-schemas';
|
||||
|
||||
/**
|
||||
* Sanitizes the model name to be used in the URL by removing or replacing disallowed characters.
|
||||
|
|
@ -118,3 +120,43 @@ export function constructAzureURL({
|
|||
|
||||
return finalURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Entra ID authentication should be used based on environment variables.
|
||||
* @returns {boolean} True if Entra ID authentication should be used
|
||||
*/
|
||||
export const shouldUseEntraId = (): boolean => {
|
||||
return process.env.AZURE_OPENAI_USE_ENTRA_ID === 'true';
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an Azure credential for Entra ID authentication.
|
||||
* Uses DefaultAzureCredential which supports multiple authentication methods:
|
||||
* - Managed Identity (when running in Azure)
|
||||
* - Service Principal (when environment variables are set)
|
||||
* - Azure CLI (for local development)
|
||||
* - Visual Studio Code (for local development)
|
||||
*
|
||||
* @returns DefaultAzureCredential instance
|
||||
*/
|
||||
|
||||
export const createEntraIdCredential = (): DefaultAzureCredential => {
|
||||
return new DefaultAzureCredential();
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the access token for Entra ID authentication from azure/identity.
|
||||
* @returns {Promise<AccessToken>} The access token
|
||||
*/
|
||||
export const getEntraIdAccessToken = async (): Promise<string> => {
|
||||
try {
|
||||
const credential = createEntraIdCredential();
|
||||
|
||||
const tokenResponse = await credential.getToken('https://cognitiveservices.azure.com/.default');
|
||||
|
||||
return tokenResponse.token;
|
||||
} catch (error) {
|
||||
logger.error('[ENTRA_ID_DEBUG] Failed to get Entra ID access token:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue