mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-11 20:05:19 +00:00
feat: Allow Azure OpenAI to authenticate using DefaultAzureCredential
Co-Authored-By: Selene Blok <20491756+selenecodes@users.noreply.github.com>
This commit is contained in:
parent
72cd3a54f7
commit
caf0a1fbb6
2 changed files with 49 additions and 11 deletions
|
|
@ -9,6 +9,8 @@ from aiocache import cached
|
||||||
import requests
|
import requests
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException, Request, APIRouter
|
from fastapi import Depends, HTTPException, Request, APIRouter
|
||||||
from fastapi.responses import (
|
from fastapi.responses import (
|
||||||
FileResponse,
|
FileResponse,
|
||||||
|
|
@ -182,12 +184,30 @@ def get_headers_and_cookies(
|
||||||
if oauth_token:
|
if oauth_token:
|
||||||
token = f"{oauth_token.get('access_token', '')}"
|
token = f"{oauth_token.get('access_token', '')}"
|
||||||
|
|
||||||
|
elif auth_type in ("azure_ad", "azure_entra_id"):
|
||||||
|
token = get_azure_entra_id_access_token()
|
||||||
|
|
||||||
if token:
|
if token:
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
headers["Authorization"] = f"Bearer {token}"
|
||||||
|
|
||||||
return headers, cookies
|
return headers, cookies
|
||||||
|
|
||||||
|
|
||||||
|
def get_azure_entra_id_access_token():
|
||||||
|
"""
|
||||||
|
Get Azure access token using DefaultAzureCredential for Azure OpenAI.
|
||||||
|
Returns the token string or None if authentication fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
token_provider = get_bearer_token_provider(
|
||||||
|
DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
|
||||||
|
)
|
||||||
|
return token_provider()
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Error getting Azure access token: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
#
|
#
|
||||||
# API routes
|
# API routes
|
||||||
|
|
@ -641,9 +661,12 @@ async def verify_connection(
|
||||||
)
|
)
|
||||||
|
|
||||||
if api_config.get("azure", False):
|
if api_config.get("azure", False):
|
||||||
headers["api-key"] = key
|
# Only set api-key header if not using Azure Entra ID authentication
|
||||||
api_version = api_config.get("api_version", "") or "2023-03-15-preview"
|
auth_type = api_config.get("auth_type", "bearer")
|
||||||
|
if auth_type not in ("azure_ad", "azure_entra_id"):
|
||||||
|
headers["api-key"] = key
|
||||||
|
|
||||||
|
api_version = api_config.get("api_version", "") or "2023-03-15-preview"
|
||||||
async with session.get(
|
async with session.get(
|
||||||
url=f"{url}/openai/models?api-version={api_version}",
|
url=f"{url}/openai/models?api-version={api_version}",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
|
@ -885,7 +908,12 @@ async def generate_chat_completion(
|
||||||
if api_config.get("azure", False):
|
if api_config.get("azure", False):
|
||||||
api_version = api_config.get("api_version", "2023-03-15-preview")
|
api_version = api_config.get("api_version", "2023-03-15-preview")
|
||||||
request_url, payload = convert_to_azure_payload(url, payload, api_version)
|
request_url, payload = convert_to_azure_payload(url, payload, api_version)
|
||||||
headers["api-key"] = key
|
|
||||||
|
# Only set api-key header if not using Azure Entra ID authentication
|
||||||
|
auth_type = api_config.get("auth_type", "bearer")
|
||||||
|
if auth_type not in ("azure_ad", "azure_entra_id"):
|
||||||
|
headers["api-key"] = key
|
||||||
|
|
||||||
headers["api-version"] = api_version
|
headers["api-version"] = api_version
|
||||||
request_url = f"{request_url}/chat/completions?api-version={api_version}"
|
request_url = f"{request_url}/chat/completions?api-version={api_version}"
|
||||||
else:
|
else:
|
||||||
|
|
@ -1058,7 +1086,12 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||||
|
|
||||||
if api_config.get("azure", False):
|
if api_config.get("azure", False):
|
||||||
api_version = api_config.get("api_version", "2023-03-15-preview")
|
api_version = api_config.get("api_version", "2023-03-15-preview")
|
||||||
headers["api-key"] = key
|
|
||||||
|
# Only set api-key header if not using Azure Entra ID authentication
|
||||||
|
auth_type = api_config.get("auth_type", "bearer")
|
||||||
|
if auth_type not in ("azure_ad", "azure_entra_id"):
|
||||||
|
headers["api-key"] = key
|
||||||
|
|
||||||
headers["api-version"] = api_version
|
headers["api-version"] = api_version
|
||||||
|
|
||||||
payload = json.loads(body)
|
payload = json.loads(body)
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key) {
|
if (!key && !['azure_ad', 'azure_entra_id'].includes(auth_type)) {
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
||||||
toast.error($i18n.t('Key is required'));
|
toast.error($i18n.t('Key is required'));
|
||||||
|
|
@ -331,6 +331,9 @@
|
||||||
<option value="session">{$i18n.t('Session')}</option>
|
<option value="session">{$i18n.t('Session')}</option>
|
||||||
{#if !direct}
|
{#if !direct}
|
||||||
<option value="system_oauth">{$i18n.t('OAuth')}</option>
|
<option value="system_oauth">{$i18n.t('OAuth')}</option>
|
||||||
|
{#if azure}
|
||||||
|
<option value="azure_entra_id">{$i18n.t('Azure Entra ID')}</option>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -361,6 +364,12 @@
|
||||||
>
|
>
|
||||||
{$i18n.t('Forwards system user OAuth access token to authenticate')}
|
{$i18n.t('Forwards system user OAuth access token to authenticate')}
|
||||||
</div>
|
</div>
|
||||||
|
{:else if ['azure_ad', 'azure_entra_id'].includes(auth_type)}
|
||||||
|
<div
|
||||||
|
class={`text-xs self-center translate-y-[1px] ${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : 'text-gray-500'}`}
|
||||||
|
>
|
||||||
|
{$i18n.t('Uses DefaultAzureCredential to authenticate')}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -443,7 +452,7 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full mt-2">
|
||||||
<div class="mb-1 flex justify-between">
|
<div class="mb-1 flex justify-between">
|
||||||
<div
|
<div
|
||||||
class={`mb-0.5 text-xs text-gray-500
|
class={`mb-0.5 text-xs text-gray-500
|
||||||
|
|
@ -499,8 +508,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class=" border-gray-100 dark:border-gray-700/10 my-1.5 w-full" />
|
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<label class="sr-only" for="add-model-id-input">{$i18n.t('Add a model ID')}</label>
|
<label class="sr-only" for="add-model-id-input">{$i18n.t('Add a model ID')}</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -528,9 +535,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class=" border-gray-50 dark:border-gray-850 my-2.5 w-full" />
|
<div class="flex gap-2 mt-2">
|
||||||
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<div
|
<div
|
||||||
class={`mb-0.5 text-xs text-gray-500
|
class={`mb-0.5 text-xs text-gray-500
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue