从Windows桌面应用程序通过AD授权调用Azure函数

问题描述 投票:0回答:1

GOAL:我想要一个可以通过Windows桌面应用程序调用的Azure函数(HttpTrigger)。我希望对功能的访问由Active Directory控制,只有经过授权的用户才能调用它。

当前状态:我按照指南here创建具有AD授权的桌面应用。我还创建了一个Azure函数,向其中添加了“使用Azure Active Directory登录”的“应用程序服务身份验证”,并创建了一个新的应用程序注册来处理此问题。在我的桌面应用程序中,我添加了一个调用此功能的按钮。

问题:当我通过浏览器中的链接直接调用该函数时,一切运行正常。如果我被授权,它将调用该函数;如果不是,则将我重定向到登录屏幕,并在成功登录后(仅针对授权用户)获得函数的结果。当我尝试通过桌面应用程序执行此操作时,就会出现问题。当我按下函数调用按钮时,我将重定向到登录屏幕,并且一旦我成功使用自己的凭据登录,就会收到错误消息:

AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: <app-id>

[这会在我的应用程序注册中没有针对“移动和桌面应用程序”的身份验证选项,而仅针对“ Web”的身份验证选项。如果我添加了“移动和桌面应用程序”选项,则原来的按钮(来自上面的教程)可以登录并正常工作(在以前的情况下,它给我同样的错误),但是这次,当我尝试通过我添加的按钮调用该函数,程序因错误而崩溃:

Inner Exception 1:
HttpRequestException: An error occurred while sending the request.

Inner Exception 2:
WebException: The underlying connection was closed: An unexpected error occurred on a send.

Inner Exception 3:
IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

Inner Exception 4:
SocketException: An existing connection was forcibly closed by the remote host

如果强制使用TLS 1.2,则会出现401错误:“您无权查看此目录或页面。”。如果我尝试调用不使用AD授权的函数,则整个过程将成功。我的代码:

        private async void CallFunctionButton_Click(object sender, RoutedEventArgs e)
        {
            AuthenticationResult authResult = null;
            var app = App.PublicClientApp;
            ResultText.Text = string.Empty;
            TokenInfoText.Text = string.Empty;

            var accounts = await app.GetAccountsAsync();
            var firstAccount = accounts.FirstOrDefault();

            try
            {
                authResult = await app.AcquireTokenSilent(scopes, firstAccount)
                    .ExecuteAsync();
            }
            catch (MsalUiRequiredException ex)
            {
                System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

                try
                {
                    authResult = await app.AcquireTokenInteractive(scopes)
                        .WithAccount(accounts.FirstOrDefault())
                        .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle)
                        .WithPrompt(Prompt.SelectAccount)
                        .ExecuteAsync();
                }
                catch (MsalException msalex)
                {
                    ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
                }
            }
            catch (Exception ex)
            {
                ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
                return;
            }

            if (authResult != null)
            {
                this.SignOutButton.Visibility = Visibility.Visible;
                string token = authResult.AccessToken;

                using (var client = new HttpClient())
                {
                    // With an explicit selection of the security protocol the program does not crash.
                    // Instead it gives 401 Unauthorized error, when already signed in.
                    // Without the following line, the program crashes.
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;


                    string requestUrl = $"the_URL_of_my_function";

                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

                    HttpResponseMessage response = client.SendAsync(request).Result;
                    var responseString = response.Content.ReadAsStringAsync().Result;
                    ResultText.Text = responseString;
                    DisplayBasicTokenInfo(authResult);
                }
            }
        }

问题:我可以通过Windows桌面应用程序调用/使用需要授权的Azure功能吗?

azure azure-active-directory authorization azure-functions desktop-application
1个回答
0
投票

关于此问题,它可能与TLS版本有关。据我所知,此刻,Azure App Service will be created with TLS 1.2 by default。但是,WPF应用程序默认使用TLS 1.0。因此,我们无法调用Azure函数。关于如何修复,请参考document


更新

关于如何调用Azure AD投影的Azure函数,请参考以下步骤

  1. 为Azure功能配置Azure ADenter image description here

  2. 在Azure AD中创建客户端应用程序

  3. 配置API权限并获取我们需要的范围enter image description hereenter image description here

  4. code

string[] scopes = new string[] 
{"https://testfun08.azurewebsites.net/user_impersonation" };// the scope you copy

 private async void CallFunctionButton_Click(object sender, RoutedEventArgs e)
        {
      // get token
          AuthenticationResult authResult = null;
            var app = App.PublicClientApp;
            ResultText.Text = string.Empty;
            TokenInfoText.Text = string.Empty;

            var accounts = await app.GetAccountsAsync();
            var firstAccount = accounts.FirstOrDefault();

            try
            {
                authResult = await app.AcquireTokenSilent(scopes, firstAccount)
                    .ExecuteAsync();
            }
            catch (MsalUiRequiredException ex)
            {
                System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

                try
                {
                    authResult = await app.AcquireTokenInteractive(scopes)
                        .WithAccount(accounts.FirstOrDefault())
                        .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle)
                        .WithPrompt(Prompt.SelectAccount)
                        .ExecuteAsync();
                }
                catch (MsalException msalex)
                {
                    ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
                }
            }
            catch (Exception ex)
            {
                ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
                return;
            }

       //call Azure function
            if (authResult != null)
            {
                this.SignOutButton.Visibility = Visibility.Visible;
                string token = authResult.AccessToken;

                using (var client = new HttpClient())
                {
                    // Without the following line, the program crashes.
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;


                    string requestUrl = $"the_URL_of_my_function";
                 client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);


                    HttpResponseMessage response = client.SendAsync(request).Result;
                    var responseString = response.Content.ReadAsStringAsync().Result;
                    ResultText.Text = responseString;
                    DisplayBasicTokenInfo(authResult);
                }
            }
        }

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.