Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure Service Principal with Secret authentication workflow. #5131

Conversation

kzych-inpost
Copy link
Contributor

Context

To my best knowledge (version v1.42.12), the only way to authentiacate using Service Principal workflow is to provide azure_ad_token - either hardcoded (token expires though) or providing a path that starts with oidc/:

model_name: "azure-gpt-35"
    litellm_params:
      model: "azure/gpt-35-turbo"
      api_base: "some-url"
      azure_ad_token: "oidc/azure/api://AzureADTokenExchange"

Why?

However, this workflow does not suit my needs, as it requires AZURE_FEDERATED_TOKEN_FILE.
My goal is to use Service Principal with Secret workflow.

How?

There is a possibility in openai SDK to use azure_ad_token_provider parameter, which is not used yet in the LiteLLM Proxy.

Relevant issues

Related issue: #4417.

Type

🆕 New Feature

Changes

  • introduced get_azure_ad_token_provider() function;
  • modified set_client() so it uses get_azure_ad_token_provider() when necessary.

[REQUIRED] Testing - Attach a screenshot of any new tests passing locall

If UI changes, send a screenshot/GIF of working UI fixes

❯ poetry run pytest -q --disable-warnings .
sssssssssssssssssssssssssssssssssssssssssssssssssssssssssss....ssssssssssssssssssss                                           [100%]
4 passed, 79 skipped, 143 warnings in 4.61s

Copy link

vercel bot commented Aug 9, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
litellm ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 20, 2024 11:18am

@kzych-inpost
Copy link
Contributor Author

kzych-inpost commented Aug 9, 2024

New usage would be simple, without any azure_ad_token key:

model_name: "azure-gpt-35"
    litellm_params:
      model: "azure/gpt-35-turbo"
      api_base: "some-url"

but these environment variables need to be passed: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET.

Copy link
Contributor

@ishaan-jaff ishaan-jaff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @kzych-inpost can you send a screenshot of using azure_ad_token_provider working for your on your proxy server ?

@kzych-inpost
Copy link
Contributor Author

Oh right, hope these screenshots would suffice:

docker-compose.yml - two important lines:

  1. line 81 - using image built locally with my change;
  2. lines 94-96 - environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET are used by LiteLLM Proxy
Screenshot 2024-08-12 at 10 52 07

litellm-config.yaml - defined models from my Azure deployments:
Screenshot 2024-08-12 at 11 03 03

and here is the docker output with successful requests:
Screenshot 2024-08-12 at 10 52 47

@krrishdholakia
Copy link
Contributor

@ishaan-jaff is this good for merge?

@shulkx
Copy link

shulkx commented Aug 14, 2024

Hopefully, it will be merged soon and then there will no need to refresh the token schedulely anymore when using the model from azure.

@Manouchehri

This comment was marked as outdated.

@Manouchehri

This comment was marked as outdated.

@Manouchehri
Copy link
Collaborator

This has a really high risk of moving people away from the more secure OIDC flow, and encouraging them to use static secrets instead of more-secure temporary credentials. I would really want to see #4417 fixed before this is merged in.

Also, I really don't like that this is loading way more than the title implies. See this huge list for all the things it's doing:

https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet

I would really want to see this narrowed down to just ClientSecretCredential.

https://learn.microsoft.com/en-us/python/api/azure-identity/azure.identity.clientsecretcredential?view=azure-python

And finally, get_azure_ad_token_provider is an incredibly misleading name, and will cause people to think that this is the only recommended way of doing auth to Azure, instead of using the more secure (and tested) OIDC flow. At the least, please do not merge this until get_azure_ad_token_provider is removed or renamed to something less misleading.

Copy link
Collaborator

@Manouchehri Manouchehri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments in #5131 (comment).

@kzych-inpost
Copy link
Contributor Author

@Manouchehri:

This has a really high risk of moving people away from the more secure OIDC flow, and encouraging them to use static secrets instead of more-secure temporary credentials.

I see your point, but there are use-cases (like mine - Service Principal authentication), where classic OIDC flow for user authentication is not what suits one's need. What is more, the authentication method I've proposed also incorporates fetching temporary token (temporary credential mentioned by you), so I believe this method is also quite secure.

I would really want to see this narrowed down to just ClientSecretCredential.

Sure, I can do that.
On the other hand, authenticating using EnvironmentCredential would make Service Principal authentication more flexible - it would be possible not only for Service principal with secret flow, but also Service principal with certificate and User with username and password. What do you think?

get_azure_ad_token_provider is an incredibly misleading name

Well, azure_ad_token_provider is an exact name of a parameter used in openai.AsyncAzureOpenAI (link). Don't know how can I make the function get_azure_ad_token_provider less misleading, it basically gets the azure_ad_token_provider (the function that fetches the temporary token I mentioned above) required by openai.AsyncAzureOpenAI.

@kzych-inpost
Copy link
Contributor Author

To add more context: advantage of this solution is that it passes the responsibility for Azure authentication to Azure SDK.

import os
from typing import Callable

from azure.identity import DefaultAzureCredential
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move the azure. imports to inside the function. This way it's only required, if this approach is used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -186,9 +187,16 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict):
f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}"
)
azure_ad_token = litellm_params.get("azure_ad_token")
azure_ad_token_provider = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we type this correctly, so it's clear what the potential values could be here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -303,6 +315,7 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict):
"azure_endpoint": api_base,
"api_version": api_version,
"azure_ad_token": azure_ad_token,
"azure_ad_token_provider": azure_ad_token_provider,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a basic unit test here -

async def test_router_init():

with a mock test to make sure there's no future regressions

Copy link
Contributor Author

@kzych-inpost kzych-inpost Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. OK, should I make another test-function next to this one with mocking environment variables (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID)?
  2. The test you mentioned is skipped on my machine (problably due to the missing pytest-asyncio plugin and pytest.mark.asyncio decorator). Is this expected behaviour and this would be executed on CI/CD?
  3. Can't test it locally anyway because I have no redis installed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepared the commit 85a8b9e with environment variables and other prerequisites mocked.

@krrishdholakia
Copy link
Contributor

Hi @kzych-inpost looks good - left some comments re: refactoring code + Adding unit tests, before we can merge. thank you for this PR!

…zure Service Principal with Secret authentication workflow.
@kzych-inpost
Copy link
Contributor Author

How about the change in the documentation indicatiing that this feature is possible?

…g Azure Service Principal with Secret authentication workflow.
@kzych-inpost
Copy link
Contributor Author

@krrishdholakia all requested changes are already commited :)
Are we ready to merge then?

@krrishdholakia krrishdholakia changed the base branch from main to litellm_stable_merge_prs August 30, 2024 01:34
@krrishdholakia krrishdholakia merged commit 5787754 into BerriAI:litellm_stable_merge_prs Aug 30, 2024
2 checks passed
krrishdholakia added a commit that referenced this pull request Sep 2, 2024
…5437)

* Azure Service Principal with Secret authentication workflow. (#5131)

* Implement Azure Service Principal with Secret authentication workflow.

* Use `ClientSecretCredential` instead of `DefaultAzureCredential`.

* Move imports into the function.

* Add type hint for `azure_ad_token_provider`.

* Add unit test for router initialization and sample completion using Azure Service Principal with Secret authentication workflow.

* Add unit test for router initialization with neither API key nor using Azure Service Principal with Secret authentication workflow.

* fix(client_initializtion_utils.py): fix typing + overrides

* test: fix linting errors

* fix(client_initialization_utils.py): fix client init azure ad token logic

* fix(router_client_initialization.py): add flag check for reading azure ad token from environment

* test(test_streaming.py): skip end of life bedrock model

* test(test_router_client_init.py): add correct flag to test

---------

Co-authored-by: kzych-inpost <142029278+kzych-inpost@users.noreply.github.com>
@kzych-inpost kzych-inpost deleted the feature/service_principal_with_secret_workflow branch September 4, 2024 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants