Using AzureOpenAIClient with LINQPadTokenCredential
Hi,
I'm trying to get the sample from the new Azure OpenAI library for .NET (here) going in LINQPad using creadentials (rather than the api call).
The problem I'm hitting is that when GetTokenAsync is called and the authentication window pops up it is blank:
If I open dev tools on the window and hit reload, I can see a request error which says that I don't have the proper consent.
I'm not clear on whether I need to add scopes and if so how I would integrate that into LINQPadTokenCredential as it's the ChatClient.CompleteChat call that appears to invoke GetToken?
Am I doing something obviously wrong here?
Many thanks
John
Pasting code below:
// using Azure.AI.OpenAI package version 2.0.0 void Main() { // Based on: // https://devblogs.microsoft.com/azure-sdk/announcing-the-stable-release-of-the-azure-openai-library-for-net/ string authEndPoint = Util.AzureCloud.PublicCloud.AuthenticationEndpoint; string tenantID = "<my-tenant-domain>"; string userHint = $"<my-name-part>@{tenantID}"; string azureOpenAiUri = "https://<my-azure-openai-resource.com>"; //AzureOpenAIClient azureClient = new( //new Uri("https://your-azure-openai-resource.com"), //new DefaultAzureCredential()); var credential = new LINQPadTokenCredential(authEndPoint + tenantID, userHint); AzureOpenAIClient azureClient = new(new Uri(azureOpenAiUri), credential); ChatClient chatClient = azureClient.GetChatClient("<my-model-deployment-name>"); ChatCompletion completion = chatClient.CompleteChat( [ // System messages represent instructions or other guidance about how the assistant should behave new SystemChatMessage("You are a helpful assistant that talks like a pirate."), // User messages represent user input, whether historical or the most recent input new UserChatMessage("Hi, can you help me?"), // Assistant messages in a request represent conversation history for responses new AssistantChatMessage("Arrr! Of course, me hearty! What can I do for ye?"), new UserChatMessage("What's the best way to train a parrot?"), ]); Console.WriteLine($"{completion.Role}: {completion.Content[0].Text}"); completion.Dump(); } class LINQPadTokenCredential : TokenCredential { public readonly string Authority, UserIDHint; public LINQPadTokenCredential(string authority, string userIDHint) => (Authority, UserIDHint) = (authority, userIDHint); public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) => GetTokenAsync(requestContext, cancellationToken).Result; public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancelToken) { // Call LINQPad's AcquireTokenAsync method to authenticate interactively, and cache token in the LINQPad GUI. var auth = await Util.MSAL.AcquireTokenAsync(Authority, requestContext.Scopes, UserIDHint).ConfigureAwait(false); return new AccessToken(auth.AccessToken, auth.ExpiresOn); } }
Comments
Does the following help?
https://forum.linqpad.net/discussion/comment/8258#Comment_8258
Thanks for your reply Joe. I did try this before and it's where I discovered LINQPadTokenCredential. I've discovered that a single scope of "https://cognitiveservices.azure.com/.default" is being passed in with the context, but I'm still hitting this 65002 error via dev tools. In Entra sign-in logs I can see the errror (65002) which has the following details:
Failure reason - Consent between first party application '{applicationId}' and first party resource '{resourceId}' must be configured via preauthorization - applications owned and operated by Microsoft must get approval from the API owner before requesting tokens for that API.
Additional details - A developer in your tenant may be attempting to reuse an App ID owned by Microsoft. This error prevents them from impersonating a Microsoft application to call other APIs. They must move to another app ID they register in portal.azure.com.
Currently I'm not passing in an app ID so I'm not clear where that comes from. Anyway, I'll keep bashing at it.
Best regards
John
So thank you again. That thread did help and I'm now up and running.
I now understand that there were a number of things I was not clear on:
As per the other thread I needed to add the https://login.microsoftonline.com/common/oauth2/nativeclient url to the redirect list under App Registrations / MyApp / Authentication (if you have no redirects you hit ADDSTS500113 - No reply address is registered). This matches the redirect in the request which I'm assuming is built by Util.MSAL?
I needed to pass in the clientID (for 'MyApp') in the Util.MSAL.AcquireTokenAsync call and without this I got the blank screen above with the AADSTS65002 error (via dev tools).
Once these two items were in place I was able to authenticate and get a token, but then hit a 401 PermissionDenied, so I went back to the Azure OpenAI resource and added "Cognitive Services OpenAI Contributor" and "Cognitive Services OpenAI User" in additiona to the "Cognitive Services Contributor" that I had previously added.
So below is my working final code (where I set the prompt to require consent each time....so I could see where I was).
Thanks again Joe and other thread for your help.
Best regards
John
Thanks for sharing your solution.