【问题标题】:Call Azure Function with AD Authorization from a Windows desktop app从 Windows 桌面应用程序调用具有 AD 授权的 Azure 函数
【发布时间】:2020-04-16 10:46:36
【问题描述】:

目标: 我想要一个可以通过 Windows 桌面应用程序调用的 Azure Functions (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】:

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


    更新

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

    1. 为 Azure Function 配置 Azure AD

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

    3. 配置API权限并获取我们需要的范围

    4. 代码

    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);
                    }
                }
            }
    
    

    【讨论】:

    • 我已经尝试强制使用 TLS 1.2。它仍然不起作用,尽管这次我收到了 401 未经授权的错误(当我传递从 AD 获取的令牌时)。
    • @SpyridonNonServiam 您能否告诉我您在调用 Azure 函数时是否提供了 AD 访问令牌?
    • 问题确实出在 API 权限和相关范围内。谢谢。
    猜你喜欢
    • 2022-06-24
    • 2020-06-18
    • 2018-11-14
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    相关资源
    最近更新 更多